Hi Jun. I think you are getting at the fact that OAuth 2 is a flexible framework that allows different installations to do things differently. It is true that the principal name in Kafka could come from any claim in the token. Most of the time it would come from the 'sub' claim, but it could certainly come from another claim, or it could be only indirectly based on a claim value (maybe certain text would be trimmed or prefixed/suffixed). The point, which I think you are getting at, is that because the framework is flexible we need to accommodate that flexibility. The callback handler class defined by the listener.name.sasl_ssl.oauthbearer.sasl.server. callback.handler.class configuration value gives us the required flexibility. As an example, I have an implementation that leverages a popular open source JOSE library to parse the compact serialization, retrieve the public key if it has not yet been retrieved, verify the digital signature, and map the 'sub' claim to the OAuthBearerToken's principal name (which becomes the SASL authorization ID, which becomes the Kafka principal name). I could just as easily have mapped a different claim to the OAuthBearerToken's principal name, manipulated the 'sub' claim value in some way, etc. I write the callback handler code, so I complete flexibility to do whatever my OAuth 2 installation requires me to do.
Ron On Tue, May 15, 2018 at 1:39 PM, Jun Rao <j...@confluent.io> wrote: > Hi, Ron, > > Thanks for the reply. I understood your answers to #2 and #3. > > For #1, will the server map all clients' principal name to the value > associated with "sub" claim? How do we support mapping different clients to > different principal names? > > Jun > > On Mon, May 14, 2018 at 7:02 PM, Ron Dagostino <rndg...@gmail.com> wrote: > > > Hi Jun. Thanks for the +1 vote. > > > > Regarding the first question about token claims, yes, you have it correct > > about translating the OAuth token to a principle name via a JAAS module > > option in the default unsecured case. Specifically, the OAuth SASL > Server > > implementation is responsible for setting the authorization ID, and it > gets > > the authorization ID from the OAuthBearerToken's principalName() method. > > The listener.name.sasl_ssl.oauthbearer.sasl.server. > callback.handler.class > > is responsible for handling an instance of OAuthBearerValidatorCallback > to > > accept a token compact serialization from the client and return an > instance > > of OAuthBearerToken (assuming the compact serialization validates), and > in > > the default unsecured case the builtin unsecured validator callback > handler > > defines the OAuthBearerToken.principalName() method to return the 'sub' > > claim value by default (with the actual claim it uses being configurable > > via the unsecuredValidatorPrincipalClaimName JAAS module option). So > that > > is how we translate from a token to a principal name in the default > > unsecured (out-of-the-box) case. > > > > For production use cases, the implementation associated with > > listener.name.sasl_ssl.oauthbearer.sasl.server.callback.handler.class > can > > do whatever it wants. As an example, I have written a class that wraps a > > com.nimbusds.jwt.SignedJWT instance (see > > https://connect2id.com/products/nimbus-jose-jwt/) and presents it as an > > OAuthBearerToken: > > > > public class NimbusSignedJwtOAuthBearerToken implements > OAuthBearerToken { > > private final SignedJWT signedJwt; > > private final String principalName; > > private final Set<String> scope; > > private final Long startTimeMs; > > private final long lifetimeMs; > > > > /** > > * Constructor > > * > > * @param signedJwt > > * the mandatory signed JWT > > * @param principalClaimName > > * the mandatory claim name identifying the claim from > which > > the > > * principal name will be extracted. The claim must exist > > and must be > > * a String. > > * @param optionalScopeClaimName > > * the optional claim name identifying the claim from > which > > any scope > > * will be extracted. If specified and the claim exists > then > > the > > * value must be either a String or a String List. > > * @throws ParseException > > * if the principal claim does not exist or is not a > > String; the > > * scope claim is neither a String nor a String List; the > > 'exp' > > * claim does not exist or is not a number; the 'iat' > claim > > exists > > * but is not a number; or the 'nbf' claim exists and is > > not a > > * number. > > */ > > public NimbusSignedJwtOAuthBearerToken(SignedJWT signedJwt, String > > principalClaimName, > > String optionalScopeClaimName) throws ParseException { > > // etc... > > } > > > > The callback handler runs the following code if the digital signature > > validates: > > > > callback.token(new NimbusSignedJwtOAuthBearerToken(signedJwt, "sub", > > null)); > > > > I hope that answers the first question. If not let me know what I > > missed/misunderstood and I'll be glad to try to address it. > > > > Regarding the second question, the classes OAuthBearerTokenCallback and > > OAuthBearerValidatorCallback implement the Callback interface -- they are > > the callbacks that the AuthenticateCallbackHandler implementations need > to > > handle. Specifically, unless the unsecured functionality is what is > > desired, the two configuration values [listener.name.sasl_ssl. > oauthbearer. > > ]sasl.login.callback.handler.class and > > listener.name.sasl_ssl.oauthbearer.sasl.server.callback.handler.class > > define the callback handlers that need to handle OAuthBearerTokenCallback > > and OAuthBearerValidatorCallback, respectively. > > > > Regarding the third question, yes, I see your point that the way the spec > > is worded could be taken to imply that the error code is a single > > character: "A single ASCII..." ( > > https://tools.ietf.org/html/rfc6749#section-5.2). However, it is not a > > single character. See the end of that section 5.2 for an example that > > shows "error":"invalid_request" as the response. > > > > Thanks again for the +1 vote, Jun, and please do let me know if I can > cover > > anything else. > > > > Ron > > > > > > On Mon, May 14, 2018 at 7:10 PM, Jun Rao <j...@confluent.io> wrote: > > > > > Hi, Ron, > > > > > > Thanks for the KIP. +1 from me. Just a few minor comments below. > > > > > > 1. It seems that we can translate an OAuth token to a principle name > > > through the claim name configured in JASS. However, it's not clear to > me > > > how an OAuth token is mapped to a claim. Could you clarify that? > > > > > > 2. The wiki has the following code. It seems that > > OAuthBearerTokenCallback > > > should implement AuthenticateCallbackHandler? Ditto > > > for OAuthBearerValidatorCallback. > > > > > > public class OAuthBearerTokenCallback implements Callback > > > > > > 3. In OAuthBearerTokenCallback, we have the following method. The OAuth > > > spec says the error code is a single ASCII. So, should we return a Char > > or > > > a String? > > > > > > public String errorCode() > > > > > > Jun > > > > > > > > > On Thu, May 3, 2018 at 8:55 PM, Ron Dagostino <rndg...@gmail.com> > wrote: > > > > > > > Hi everyone. I would like to start the vote for KIP-255: > > > > https://cwiki.apache.org/confluence/pages/viewpage. > > > action?pageId=75968876 > > > > > > > > This KIP proposes to add the following functionality related to > > > > SASL/OAUTHBEARER: > > > > > > > > 1) Allow clients (both brokers when SASL/OAUTHBEARER is the > > inter-broker > > > > protocol as well as non-broker clients) to flexibly retrieve an > access > > > > token from an OAuth 2 authorization server based on the declaration > of > > a > > > > custom login CallbackHandler implementation and have that access > token > > > > transparently and automatically transmitted to a broker for > > > authentication. > > > > > > > > 2) Allow brokers to flexibly validate provided access tokens when a > > > client > > > > establishes a connection based on the declaration of a custom SASL > > Server > > > > CallbackHandler implementation. > > > > > > > > 3) Provide implementations of the above retrieval and validation > > features > > > > based on an unsecured JSON Web Token that function out-of-the-box > with > > > > minimal configuration required (i.e. implementations of the two types > > of > > > > callback handlers mentioned above will be used by default with no > need > > to > > > > explicitly declare them). > > > > > > > > 4) Allow clients (both brokers when SASL/OAUTHBEARER is the > > inter-broker > > > > protocol as well as non-broker clients) to transparently retrieve a > new > > > > access token in the background before the existing access token > expires > > > in > > > > case the client has to open new connections. > > > > > > > > Thanks, > > > > > > > > Ron > > > > > > > > > >