grpc-spring
grpc-spring copied to clipboard
An Authentication object was not found in the SecurityContext
Hi. I trying to use annotation @security checks, my config:
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
class Configuration {
@Bean
fun grpcAuthenticationReader(projectRepository: ProjectRepository): GrpcAuthenticationReader {
return ApiTokenAuthenticationReader(projectRepository)
}
@GrpcGlobalServerInterceptor
fun securityContextCoroutineContextServerInterceptor(): ServerInterceptor {
return object : CoroutineContextServerInterceptor() {
override fun coroutineContext(call: ServerCall<*, *>, headers: Metadata): CoroutineContext {
return Dispatchers.Default + SecurityCoroutineContext()
}
}
}
}
Also added SecurityCoroutineContext
from here https://github.com/yidongnan/grpc-spring-boot-starter/issues/462#issuecomment-921140929
Service
@GrpcService
class FileService() : FileServiceGrpcKt.FileServiceCoroutineImplBase() {
@Secured("ROLE_PROJECT")
override suspend fun getInfo(request: FileInfoRequest): FileInfoResponse {
....
}
}
But error occurs
An Authentication object was not found in the SecurityContext
The method ApiTokenAuthenticationReader.readAuthentication
was not called, therefore I suspect that problem not in my reader.
The question
- what did i miss?
- it's possible to get Principal inside a rpc method?
Stacktraces and logs
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:336) ~[spring-security-core-5.6.1.jar:5.6.1]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:200) ~[spring-security-core-5.6.1.jar:5.6.1]
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:58) ~[spring-security-core-5.6.1.jar:5.6.1]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.14.jar:5.3.14]
at biz.m.f.grpc.FileService$$EnhancerBySpringCGLIB$$9b66f240.getInfo(<generated>) ~[281ddc99-6f03-48c8-ac53-fe4834233627/:na]
at f.api.FileServiceGrpcKt$FileServiceCoroutineImplBase$bindService$1.invoke(FileServiceOuterClassGrpcKt.kt:153) ~[281ddc99-6f03-48c8-ac53-fe4834233627/:na]
at f.api.FileServiceGrpcKt$FileServiceCoroutineImplBase$bindService$1.invoke(FileServiceOuterClassGrpcKt.kt:153) ~[281ddc99-6f03-48c8-ac53-fe4834233627/:na]
at io.grpc.kotlin.ServerCalls$unaryServerMethodDefinition$2$$special$$inlined$map$1$2.emit(Collect.kt:139) ~[grpc-kotlin-stub-1.2.0.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:77) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:59) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at io.grpc.kotlin.HelpersKt$singleOrStatusFlow$1$invokeSuspend$$inlined$collect$1.emit(Collect.kt:139) ~[grpc-kotlin-stub-1.2.0.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:77) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:59) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at io.grpc.kotlin.ServerCalls$serverCallListener$requests$1.invokeSuspend(ServerCalls.kt:227) ~[grpc-kotlin-stub-1.2.0.jar:na]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.6.10.jar:1.6.10-release-923(1.6.10)]
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) ~[kotlinx-coroutines-core-jvm-1.5.2.jar:na]
The application's environment
- Spring (boot): 2.6
- grpc-java: 1.43
- grpc-spring-boot-starter: latest
Currently these aren't implemented yet (only non reactive).
You have to convert this class to reactive for it to work https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/security/interceptors/DefaultAuthenticatingServerInterceptor.java
It would be nice, if you could contribute it once you have implemented it.
Hm, I'm sorry, it working now. I just didn't implement AuthenticationManager
.
My knowlege of spring is poor.
Context may be accessed like this, it works too.
val auth: Authentication = SecurityContextHolder.getContext().authentication
Maybe it will be better to prevent work without registered AuthenticationManager
?
I'm not sure what you are suggesting. Could you please explain your idea in more detail?
If AuthenticationManager
isn't registered in DI container then the application started but we will get the error in runtime:
An Authentication object was not found in the SecurityContext
It possible to check existence AuthenticationManager
on boot stage?
It's just an idea, maybe not correct.
AFAICT the grpc security parts only gets activated if there is such an AuthenticationManager
.
https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/autoconfigure/GrpcServerSecurityAutoConfiguration.java#L63
I don't see any grpc-spring-boot-starter lines in the stacktrace so this is a spring security/config bug.
I think it can be closed, since everything works fine? Just one thing that I added:
@GrpcGlobalServerInterceptor
fun contextCoroutineInterceptor(): ServerInterceptor {
return object : CoroutineContextServerInterceptor() {
override fun coroutineContext(call: ServerCall<*, *>, headers: Metadata): CoroutineContext {
return Dispatchers.Default + SecurityCoroutineContext()
}
}
}
class SecurityCoroutineContext(
private val securityContext: SecurityContext = SecurityContextHolder.getContext()
) : ThreadContextElement<SecurityContext?> {
companion object Key : CoroutineContext.Key<SecurityCoroutineContext>
override val key: CoroutineContext.Key<SecurityCoroutineContext> get() = Key
override fun updateThreadContext(context: CoroutineContext): SecurityContext? {
val previousSecurityContext = SecurityContextHolder.getContext()
SecurityContextHolder.setContext(securityContext)
return previousSecurityContext.takeIf { it.authentication != null }
}
override fun restoreThreadContext(context: CoroutineContext, oldState: SecurityContext?) {
if (oldState == null) {
SecurityContextHolder.clearContext()
} else {
SecurityContextHolder.setContext(oldState)
}
}
}
I don't know if this should be added to the project?
I don't know if this should be added to the project?
I leave this open until we have decided to either add it, document it or discard it.