All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4k.security.oauth.server.AuthRequestWithRequestAuthRequestExtractor.kt Maven / Gradle / Ivy

package org.http4k.security.oauth.server

import dev.forkhandles.result4k.Failure
import dev.forkhandles.result4k.Result
import dev.forkhandles.result4k.Success
import dev.forkhandles.result4k.flatMap
import dev.forkhandles.result4k.mapFailure
import dev.forkhandles.result4k.onFailure
import org.http4k.core.Request
import org.http4k.security.ResponseType.Code
import org.http4k.security.oauth.server.request.RequestJWTValidator
import org.http4k.security.oauth.server.request.RequestObject
import org.http4k.security.oauth.server.request.RequestObjectExtractor.extractRequestObjectFromJwt

class AuthRequestWithRequestAuthRequestExtractor(
    private val requestJWTValidator: RequestJWTValidator,
    private val combineAuthRequestRequestStrategy: CombineAuthRequestRequestStrategy
) : AuthRequestExtractor {

    override fun extract(request: Request): Result {

        return AuthRequestFromQueryParameters.extract(request).flatMap { authRequest ->
            val requestJwtContainer = authRequest.request
            if (requestJwtContainer != null) {
                val requestJwtValidationError = requestJWTValidator.validate(authRequest.client, requestJwtContainer)
                if (requestJwtValidationError != null) {
                    Failure(requestJwtValidationError)
                } else {
                    extractRequestObjectFromJwt(requestJwtContainer.value)
                        .mapFailure { InvalidAuthorizationRequest("Query 'request' is invalid") }
                        .flatMap { requestObject -> combineAuthRequestAndRequestObject(authRequest, requestObject) }
                }
            } else {
                Success(authRequest)
            }
        }
    }

    private fun combineAuthRequestAndRequestObject(
        authRequest: AuthRequest,
        requestObject: RequestObject
    ): Result {
        if (requestObject.client != null && authRequest.client != requestObject.client) {
            return Failure(InvalidAuthorizationRequest("'client_id' is invalid"))
        }
        return Success(authRequest.copy(
            requestObject = requestObject,
            redirectUri = nonNullValueIfExistsOrErrorIfNotEqual(
                authRequest.redirectUri,
                requestObject.redirectUri
            ).onFailure { return it },
            state = nonNullValueIfExistsOrErrorIfNotEqual(
                authRequest.state,
                requestObject.state
            ).onFailure { return it },
            nonce = nonNullValueIfExistsOrErrorIfNotEqual(
                authRequest.nonce,
                requestObject.nonce
            ).onFailure { return it },
            responseType = nonNullValueIfExistsOrErrorIfNotEqual(
                authRequest.responseType,
                requestObject.responseType
            ).onFailure { return it }
                ?: Code,
            responseMode = nonNullValueIfExistsOrErrorIfNotEqual(
                authRequest.responseMode,
                requestObject.responseMode
            ).onFailure { return it },
            scopes = nonEmptyScopeIfExistsOrErrorIfNotEqual(authRequest.scopes, requestObject.scope)
                .onFailure { return it }
        ))
    }

    private fun  nonNullValueIfExistsOrErrorIfNotEqual(
        authRequestValue: T?,
        requestObjectValue: T?
    ): Result {
        if (authRequestValue != null && requestObjectValue != null && authRequestValue != requestObjectValue) {
            return Failure(InvalidAuthorizationRequest("request object is invalid"))
        }
        return Success(combineAuthRequestRequestStrategy.combine(authRequestValue, requestObjectValue))
    }

    private fun nonEmptyScopeIfExistsOrErrorIfNotEqual(
        authRequestValue: List,
        requestObjectValue: List
    ): Result, InvalidAuthorizationRequest> {
        if (authRequestValue.isNotEmpty() && requestObjectValue.isNotEmpty() && authRequestValue.toSet() != requestObjectValue.toSet()) {
            return Failure(InvalidAuthorizationRequest("request object is invalid"))
        }
        return Success(combineAuthRequestRequestStrategy.combine(authRequestValue, requestObjectValue))
    }

    sealed interface CombineAuthRequestRequestStrategy {

        data object Combine : CombineAuthRequestRequestStrategy {
            override fun  combine(authRequestValue: T?, requestObjectValue: T?): T? =
                authRequestValue ?: requestObjectValue

            override fun  combine(authRequestValue: List, requestObjectValue: List): List =
                authRequestValue.ifEmpty { requestObjectValue }
        }

        data object AuthRequestOnly : CombineAuthRequestRequestStrategy {
            override fun  combine(authRequestValue: T?, requestObjectValue: T?): T? =
                authRequestValue

            override fun  combine(authRequestValue: List, requestObjectValue: List): List =
                authRequestValue
        }

        data object RequestObjectOnly : CombineAuthRequestRequestStrategy {
            override fun  combine(authRequestValue: T?, requestObjectValue: T?): T? =
                requestObjectValue

            override fun  combine(authRequestValue: List, requestObjectValue: List): List =
                requestObjectValue
        }

        fun  combine(authRequestValue: T?, requestObjectValue: T?): T?
        fun  combine(authRequestValue: List, requestObjectValue: List): List
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy