Skip to content

Custom OAuth Provider configuration

It is very easy to configure http4k to integrate with any OAuth2 provider who supports the Authorisation Code Grant.

Gradle setup

    compile group: "org.http4k", name: "http4k-core", version: "3.251.0"
    compile group: "org.http4k", name: "http4k-security-oauth", version: "3.251.0"

For this example, simply reconfigure the OAuthProvider instance with the correct details, and provide custom logic for persisting and retrieving the CSRF and AccessToken.

Code

package cookbook.custom_oauth_provider

import org.http4k.client.ApacheClient
import org.http4k.core.Credentials
import org.http4k.core.HttpHandler
import org.http4k.core.Method.GET
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.OK
import org.http4k.core.Uri
import org.http4k.core.then
import org.http4k.filter.ServerFilters
import org.http4k.routing.bind
import org.http4k.routing.routes
import org.http4k.security.AccessToken
import org.http4k.security.CrossSiteRequestForgeryToken
import org.http4k.security.OAuthPersistence
import org.http4k.security.OAuthProvider
import org.http4k.security.OAuthProviderConfig
import org.http4k.security.openid.Nonce
import org.http4k.server.SunHttp
import org.http4k.server.asServer

// this example shows how to configure a custom provider for the OAuth2 Auth Code Grant flow
fun main() {
    val port = 9000

    // the callback uri which is configured in our OAuth provider
    val callbackUri = Uri.of("http://localhost:$port/callback")

    // custom OAuth2 provider configuration
    val oauthProvider = OAuthProvider(
        OAuthProviderConfig(Uri.of("https://auth.chatroulette.com"),
            "/oauth2/auth", "/oauth2/token",
            Credentials("username", "somepassword"),
            Uri.of("https://api.chatroulette.com")),
        ApacheClient(),
        callbackUri,
        listOf("emailScope", "nameScope", "familyScope"),
        CustomOAuthPersistence()
    )

    val app: HttpHandler =
        routes(
            callbackUri.path bind GET to oauthProvider.callback,
            "/" bind GET to oauthProvider.authFilter.then { Response(OK).body("hello!") }
        )

    ServerFilters.CatchAll()
        .then(app)
        .asServer(SunHttp(port)).start().block()
}

// this interface allows us to provide custom logic for storing and verifying the CSRF and AccessTokens.
// to be maximally secure, never let the end-user see the access token!
class CustomOAuthPersistence : OAuthPersistence {
    var nonce: Nonce? = null
    var csrf: CrossSiteRequestForgeryToken? = null
    var accessToken: AccessToken? = null

    override fun retrieveCsrf(request: Request): CrossSiteRequestForgeryToken? = csrf

    override fun assignCsrf(redirect: Response, csrf: CrossSiteRequestForgeryToken): Response {
        this.csrf = csrf
        return redirect.header("action", "assignCsrf")
    }

    override fun assignNonce(redirect: Response, nonce: Nonce): Response {
        this.nonce = nonce
        return redirect.header("action", "assignNonce")
    }

    override fun retrieveNonce(request: Request): Nonce? = nonce

    override fun retrieveToken(request: Request): AccessToken? = accessToken

    override fun assignToken(request: Request, redirect: Response, accessToken: AccessToken): Response {
        this.accessToken = accessToken
        return redirect.header("action", "assignToken")
    }
}