marklogic/java-client-api

Oauth behavior

Closed this issue · 6 comments

Recently support was added for OAuth context. It accepts a token (JWT token?). I assume the client passes this token when making a call to the app server supporting OAuth external security. A few questions:

  1. Is there a way to pass a new token if the one I was using expired? Unlike SAML context, there are no callbacks for oauth.
  2. Suppose I want my Java application to support many end users, each possessing a token. Do I have to create many DatabaseClient objects? Seems like the current approach works best if the client is created for a designated service user.

Hey @mhavey - for expired tokens - yes, you would need to manage this yourself. Our experience with supporting token renewal for Progress Data Cloud - you can see the code here - is that this is best done via an OkHttp interceptor. With an interceptor, you can detect the condition that indicates the token has failed, regenerate a token, and then retry the request with the new token. This examples shows how to add a custom OkHttp interceptor to the DatabaseClient.

For your second question - that's correct, since a DatabaseClient is bound to a single authentication context, you would need for each separate SAML token.

Thanks Rob. A couple followups.

  1. Is it possible to intercept the request before it is sent to MarkLogic and modify the header to add the token? That way I can use the same client but sneak in a user's JWT token just in time. It would override the existing auth context for one call. Seems like a bit of hack anyway. Just checking.
  2. Rather than creating a client up front, create a new client each time the user interacts. The release it as soon as the call is done. Seems reasonably if each call is stateless. Is there a lot of overhead in creating the client? Is this an antipattern?

Item 1 above works well - I should have mentioned that upfront. Most of our auth support in the client depends on an OkHttp interceptor doing exactly what you describe. In that sense, you could use the OAuth context with a "dummy" token, and then customize an interceptor to do whatever you would like based on the user, expiration etc. You have full access to the request in the interceptor.

You can create clients like that, but there is some overhead in terms of what OkHttp does under the hood to e.g. set up a connection pool. I think your first idea is the way to go, and it's definitely not a hack.

What I'd like to do is create a single DatabaseClient with an OAuth context set to a dummy token. Then for each request, I would like to change the Authorization header of the request to the ACTUALl token of the end user.

There are MANY end users, each having their own token. They share a DatabaseClient, but I'm hoping to effectively ignore the auth context associated with the client and override ON EACH REQUEST the authorization header. For example, suppose end user 1 has token t1 and end user 2 has token t2. When user 1 wants to make a request to MarkLogic (e.g, get a document), I want to set the Authorization header of the HTTP request to user 1's token, which is t1.

It's not hard to write the Ok HTTP intercept do change the header prior to sending the request. The examples you provided show how to do this.

The challenge is: how do I know the token in the intercept itself. The intercept's argument is chain, and from this I know the request details: url, method, headers, body. I have no way to tie the request with the token of the end user.

An enhancement that would help me do this would be a correlation ID. If I wanted to get a document, for example:

XMLDocumentManager docMgr = client.newXMLDocumentManager();
DocumentPage doc = docMgr.read(uri, corrId);

Then in the intercept, I can see corrId (perhaps as a header):

public Response intercept(Chain chain) throws IOException {
   String corrId = chain.request().header("CORRID")
   // now change Authorization header and proceed with request
}

Hope this makes sense. Happy to clarify if anything is unclear.

This is more of a Java question, and a common technique in Java applications to solve this problem is to use a ThreadLocal - https://www.baeldung.com/java-threadlocal . Spring Security does this to allow application developers to do something similar, which is accessing the authentication object for a particular thread (user).

What this might look like is that the entry point to your middle tier that authenticates a user will capture the token in a ThreadLocal object. Your OkHttp interceptor can then access that same ThreadLocal.

This is a good tip. I'm going to try it.