I am trying to accomplish authentication with JWTs...
# graphql-kotlin
m
I am trying to accomplish authentication with JWTs. here: https://github.com/ExpediaGroup/graphql-kotlin/discussions/663In it was advised to do some JWT validation work in a spring webfilter and then store that is the security context and pick that up in the GraphQl context. Is this totally necessary or could all the JWT validation logic just be dont in the GraphQLContextFactory generateContext function, as we have access to headers there?
s
@Mattlangsenkamp The validation could be done there but then how do you pass the token to the functions running your graphql operations? In that case you need to use the context.
m
that is kind of my question. Simply using the context provide by graphql-kotlin seems the simpler method to me, but if i understand correctly it is not what is being advised in the thread I posted above.
s
Sorry it was more of a leading question. That is what is being done. The only way to access data from the HTTP request in the GraphQL schema is by using the
GraphQLContext
There are multiple ways you can get the context data but it needs to be set by using the
GraphQLContextFactory
https://expediagroup.github.io/graphql-kotlin/docs/spring-server/spring-graphql-context
👍 1
r
For spring-boot with JWT:
Copy code
@Component
internal class JwtAuthenticationConverter :
    Converter<Jwt, Mono<AbstractAuthenticationToken>> {
    override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> {
        val authorities = extractAuthorities(jwt)
        val authentication = UsernamePasswordAuthenticationToken(userName, null, authorities)
        authentication.details = AuthenticationDetails(username = userName, name = name, eMail = email)
        return Mono.just(authentication)
    }
Copy code
/**
 * Provide GraphQLSecurityContext
 */
@Component
class GraphQLSecurityContextFactory : GraphQLContextFactory<GraphQLSecurityContext> {

    override suspend fun generateContext(
        request: ServerHttpRequest,
        response: ServerHttpResponse
    ): GraphQLSecurityContext {
        val reactorContext =
            coroutineContext[ReactorContext]?.context ?: throw RuntimeException("Reactor Context unavailable")
        val securityContext = reactorContext.getOrDefault<Mono<SecurityContext>>(
            SecurityContext::class.java,
            Mono.empty()
        )!!
        return GraphQLSecurityContext(securityContext = securityContext.awaitFirstOrNull())
    }
}
for my use-case, I created a custom directive, which acts on the JWT claims
Copy code
class AuthorizationDataFetcher(
    /**
     * Original Data fetcher which should be executed
     */
    private val originalDataFetcher: DataFetcher<*>,
    /**
     * Required Permission for this Data Fetcher Field
     */
    private val requiredPermission: GraphQLPermission
) : DataFetcher<Any> {

    override fun get(environment: DataFetchingEnvironment): Any {
        val context: GraphQLSecurityContext? = environment.getContext<GraphQLSecurityContext>()
        val securityContext: SecurityContext =
            context?.securityContext ?: throw AccessDeniedException("SecurityContext not present")
        checkRoles(securityContext)
        return originalDataFetcher.get(environment)
    }
*
 * Wraps the original Data Fetcher
 * Check the Security Context for the required permission
 */
class AuthorizationDataFetcher(
    /**
     * Original Data fetcher which should be executed
     */
    private val originalDataFetcher: DataFetcher<*>,
    /**
     * Required Permission for this Data Fetcher Field
     */
    private val requiredPermission: GraphQLPermission
) : DataFetcher<Any> {

    override fun get(environment: DataFetchingEnvironment): Any {
        val context: GraphQLSecurityContext? = environment.getContext<GraphQLSecurityContext>()
        val securityContext: SecurityContext =
            context?.securityContext ?: throw AccessDeniedException("SecurityContext not present")
        checkRoles(securityContext)
        return originalDataFetcher.get(environment)
    }