Implementing stateless OAuth
I am going through the OAuth examples here:
- https://www.http4k.org/guide/modules/oauth/
- https://www.http4k.org/cookbook/custom_oauth_provider/
But both of them propose implementation as a filter, meaning they protect certain resource. Both examples somehow store the fact that the user is logged (cookie/header) and let them visit the resource.
Instead of redirecting to the protected resource (in this case /), I would like to directly respond with custom JSON response immediately after the token callback. This response requires working with the authorization callback response, so it is too late to build it after the redirect back to the / where the authorization started.
Is this feasible with the oauth module?
I am assuming you are referring to a callback from an implicit grant or an authorization code grant. I had to do something similar in the past but it honestly felt really awkward.
What I did was implementing a custom OAuthPersistence similar to the InsecureCookieBasedOAuthPersistence. However instead of storing the access token, I overwrote the retrieveToken and assignToken methods similar to this:
override fun retrieveToken(request: Request): AccessToken? = request.header("Authorization")?.removePrefix("Bearer ")?.let(::AccessToken)
override fun assignToken(request: Request, redirect: Response, accessToken: AccessToken): Response {
val internalAuthorizedRequest = Request(GET, Header.LOCATION(redirect)).header("Authorization", "Bearer ${accessToken.value}")
return getApp()(internalAuthorizedRequest)
.invalidateCookie(csrfName)
.invalidateCookie(nonceName)
}
getApp is a function () -> HttpHandler passed into the OAuthPersistence that gives me the protected app with the authFilter in front. getApp has to be a function to deal with the fact that OAuthPersistence has to be created before the OAuthProvider is created:
var authProvider: OAuthProvider? = null
val protectedApp: HttpHandler by lazy { authProvider!!.authFilter.then(/** add your app here **/) })
authProvider = OAuthProvider.gitHub(
...,
oAuthPersistence = StatelessOAuthPersistence( getApp = { protectedApp }
)
val appAndOAuthCallback: HttpHandler = routes(
authProvider.callbackEndpoint,
"/" bind GET to protectedApp,
"/{:.*}" bind GET to protectedApp)
appAndOAuthCallback.asServer(Jetty(8080)).start()
At least two problems with this solution:
- It currently only works with
GETrequests to the protected resource. AnyPOSTbody or custom headers would get lost due to the redirect anyways. - I haven't found a way to get access to the
idTokenyet (only relevant with OpenID Connect)
There are probably lots of other issues with this approach :wink:
@knyttl do you want to return the resource as part of the authorization callback? Or the access token request?
Either way, I believe that goes against the standard which tries to decouple authorization server from the actual resource. But happy to consider if that's allowed by the standard (that's a new one for me).