> Normally you should setup pac4j and jax-rs so that: - a request on
> the API point is taken into account by pac4j with a first Client your
> own CredentialsExtractor and depending on your need an adequate
> Authenticator (that will check if the user/pass are valid thus).
Well, sorry but I'm already lost... there seems to be some local jargon
for which I cannot find any definition in the doc: what is an extractor?
and how can I build an authenticator object?
In your code I see
defaultAuthenticator(new CockpitAuthenticator());
so I guess I have to use something similar with the CouchDB
authenticator. But looking at the doc for CouchDB (or any other
authenticator), I see no clue about the defaultAuthenticator function,
nor any reference to an Authenticator object... This is very obscure to me!
> - if needed, a request on the rest of the API points is taken into
> account by pac4j with another Client that authenticate the request
> based on the Jwt token using a Jws Authenticator
If I manage to make a basic enpoint work, this one shouldn't be too long
to produce I guess...
> The specificity with your situation (and mine), is that we are in
> the need of a specific pac4j (indirect) client for which the
> behaviour is to extract some data from the body of the message. You
> could actually, if you are ok to use, e.g. a more typical Form POST
> or basic auth from the pov of the API caller, use the pac4j HTTP
> client (http://www.pac4j.org/2.0.x/docs/clients/http.html) and not
> bother with this extractor thing.
Well, I think I'd like to do things in a clean manner, so let's try this
extractor mechanism, I believe it's the best option
> then you need to define your own client (like this:
> https://gitlab.com/linagora/petals-cockpit/blob/master/backend/src/main/java/org/ow2/petals/cockpit/server/security/CockpitAuthClient.java)
>
>
and make your own extractor which role is to extract the user/pass (or
> any type of credential) from the body :)
Here is where I'm quite lost in fact... maybe because I'm not used
enough to Jersey: where in your code do you define the mapping between
the path ("/login" for instance) and the extractor? where is this
plumbing done?
> So maybe a simpler way to handle your situation is to use one of the
> indirect HTTP client (depending on how you are ready to constrain
> the API caller) and configure it with the couchdb authenticator (or
> make your own if needed).
This is also something I don't understand: is there a doc somewhere to
build an Authenticator object from a CouchProfileService? (or any
ProfileService?)
> This client will be registered in the pac4j configuration, and then
> you will be able to, with jax-rs-pac4j, use one of the annotations on
> the resource method definition (in the resource class) to bind it to
> the method. Basically, if the method is executed, it means the
> authentication (with respect to the authenticator) worked and then
> you can access the profile to generate a Jwt token and return it for
> example. Something like that (there may be more to it ^^).
That sounds good!
package io.bdrc.auth.server;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
import org.pac4j.core.config.Config;
import org.pac4j.couch.profile.service.CouchProfileService;
import org.pac4j.http.client.indirect.IndirectBasicAuthClient;
import org.pac4j.jax.rs.features.JaxRsConfigProvider;
import org.pac4j.jax.rs.features.Pac4JSecurityFeature;
import org.pac4j.jax.rs.jersey.features.Pac4JValueFactoryProvider;
import org.pac4j.jax.rs.servlet.features.ServletJaxRsContextFactoryProvider;
@Provider
public class MySecurityFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context
.register(new JaxRsConfigProvider(buildConfig()))
.register(new Pac4JSecurityFeature())
.register(new Pac4JValueFactoryProvider.Binder())
.register(new ServletJaxRsContextFactoryProvider());
return true;
}
private Config buildConfig() {
Config config = new Config();
CouchProfileService couch = new CouchProfileService();
// TODO configure couch...
IndirectBasicAuthClient loginClient = new IndirectBasicAuthClient(couch);
config.getClients().getClients().add(loginClient);
config.getClients().setDefaultClient(loginClient);
return config;
}
}package io.bdrc.auth.server;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.pac4j.couch.profile.CouchProfile;
import org.pac4j.jax.rs.annotations.Pac4JCallback;
import org.pac4j.jax.rs.annotations.Pac4JProfile;
@Path("login")
public class MyAuthResource {
@POST
@Pac4JCallback(skipResponse = true)
@Produces(MediaType.TEXT_PLAIN)
public String auth(@Pac4JProfile CouchProfile p) {
// pretty sure there is a better way to return it :)
return "generated token based on " + p.getId();
}
}
Thank you!
--
Elie
> This is explained here:
> https://github.com/pac4j/jax-rs-pac4j#3-protect-urls-securityfilter
> and below
In fact what I find disturbing is maybe just the vocabulary, I don't
particularly want to "protect" /login, I want to make it a login
endpoint... but I guess I just need to be used to this vocabulary...
generally speaking, I'm really feeling I'm using pac4j in a way that was
not intended, but pac4j is evolving too I guess!
> @POST @Pac4JCallback(skipResponse =true)
> @Produces(MediaType.TEXT_PLAIN)
> publicStringauth(@Pac4JProfileCouchProfilep){ // pretty sure there is
> a better way to return it :) return"generated token based on
> "+p.getId(); }
Another big puzzlement I have is: what happens if the login/password is
wrong? Does it send a 401 response? Can I control the payload?
This is not really a question but I'll just write something I think I've
undestood without being sure: in your first file defining the feature,
we define the default client. Then what triggers it in our auth method
is @Pac4JCallback, is that correct? If we wanted another client, like
jwt, we would pass the clients argument to the annotation?
Now, I'm still blocked, this time by test configuration. Basically I'm
mocking a couchdb server for testing purpose, so I need to configure
CouchProfileService with different values according to the environment.
And as I'll certainly have other variations, what I guess I want is to
create a different Config object in my testing environment. But I'm
really lost on the way to do that. My undestanding is that it's more a
jax-rs question than a pac4j question, but I actually have really hard
time finding any sort of documentation or example of that... can you
give me some clues?
> I share your feeling about using it in a way that was not intended,
> but that only concerns the authentication (in the sense of going from
> a login/pass to a jwt token for you, and a login/pass to an http
> session for me).
Indeed. I think the term "callback" is what's confusing, it's really
from a very specific point of view, I think a more general "endpoint"
would be less confusing and more generic...
> Yes it does send a 401 response and yes you can control the payload,
> but it will feel like a hack too, as before. There is room for
> improvement I guess on that side too :)
Ok thank you very much! What is the way to control the payload in case
of an error?
Le 16/06/2017 à 19:17, victo...@gmail.com a écrit :
> I will answer tomorrow to the rest of the discussion, but for this
> problem, I think it may be related to the fact that the default
> JerseyTest is not a servlet based container (but the in-memory one).
> Either you make it use a servlet based container (see for example my
> https://github.com/pac4j/jax-rs-pac4j/blob/master/src/test/java/org/pac4j/jax/rs/rules/JerseyGrizzlyServletRule.java
> as well as JerseyRule (that setups the JerseyTest), either you configure
> pac4j for not rely on a servlet container by replacing
> ServletJaxRsContextFactoryProvider with JaxRsContextFactoryProvider (but
> you won't get session support, I don't think it is so important for your
> use case).
Indeed. I've tried replacing ServletJaxRsContextFactoryProvider by
JaxRsContextFactoryProvider (I've pushed on Github), and I now have
TechnicalException: callbackUrl cannot be blank
> See
> https://github.com/pac4j/jax-rs-pac4j/tree/master/src/test/java/org/pac4j/jax/rs/rules
> for the various working combinations :)
Interesting... I have to admit I have no clue what these rules are...
are these standard Jersey things?
Thank you!
--
Elie
> In your case, there is no reason for plugin a jwt client to the
> callback url! The jwt client will serve to protect urls, not do the
> authentication on the callback url. But if you wanted to do it, then
> you would need to pass the name of the client as a parameter to the
> callback url call!
I'm not sure I'm following... but what is the limitting factor here,
pac4j or jax-rs-pac4j? I think it would make sense to open a feature
request on github, to allow broader discussion... wdyt?
> Again, that's maybe why it could be simpler NOT to use the pac4j
> callback mechanism to do the primary authentication (from login/pass
> to jwt) but only the pac4j client to to the direct (i.e., for every
> call) authentication based on the jwt token. That's a choice you
> have to make :)
Ok... I'm not entirely sure what the benefit of it would be... For
instance, with a code similar to your example, will I be able to force
JWT authentication on other URLs (like /user/get/me for instance)? If
not then I have to do as you just described...
All this is, as you can see, really not obvious for people like me who
have what I think could be qualified as a very standard and simple use
case in mind... I think there's quite a lot of room for doc improvements
here!
> Honestly I have no idea… I guess the best would be to take advantage
> of dependency injection of jersey to use the desired configuration
> based on the context? I'm not sure how that works… maybe if you put
> a Feature (like the SecurityFeature) in your test classpath, it will
> be activated only in the test context and in it you could inject
> some specific services… I'm not really sure :)
That's what I'm suspecting, I'll make a few trials in this direction.
> Could be, or at least for jax-rs-pac4j… also as you highlighted
> above, it could be interesting to be able to set the client to use
> on the callback endpoint!
This I think a github ticket should be opened, would that be ok?
> Not 100% sure, it should be doable. There has been some discussion on
> the matter in the past here:
> https://groups.google.com/d/topic/pac4j-users/EaCtHEgiWm4/discussion
> I think there is a working solution there!
Doesn't look very straightforward but I'll try it!
Thank you!
--
Elie
Thanks for your quick answers!
> Good :) you are now missing some more configuration, I think you
> simply need to fill the callback url on the config (with the url of
> your authentication endpoint, I guess /api/login?) but actually it
> won't be used: it is used for redirecting to a login page in case
> access to a protected resource as I was explaining before. Since you
> don't rely on that aspect of pac4j, you could even put some garbage
> for the callback url.
I think I've done that in
https://github.com/BuddhistDigitalResourceCenter/auth-api/commit/e320047cd29563934d74981286f3f7b26cef59e6
now I'm getting a java.lang.NullPointerException, full log on
https://pastebin.com/AXA1Rmz5
@Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
@Override
protected DeploymentContext configureDeployment() {
ResourceConfig rc = new ResourceConfig(MyResource.class);
rc.register(MyAuthResource.class);
rc.register(TestSecurityFeature.class);
return ServletDeploymentContext.forServlet(new ServletContainer(rc)).build();
} <dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<scope>test</scope>
</dependency>loginClient.setCallbackUrl("notUsed");
> Until then, I think we can hack something into jax-rs-pac4j for this
> kind of usage, maybe introducing another annotation that would be
> more "endpoint-oriented" as you said and that would setup things as
> desired :)
That would probably be a good idea!
> Yes it is true, I have not so much time for writing documentation
> (and I'm not very good at it I think... :P also I know how
> everything work, so I don't know where something is missing) so maybe
> if you have some ideas of some sentences to add to the README to make
> things smoother, don't hesitate to make a PR. No need for something
> complex, but more like some clarifications in key points where you
> know you were blocked!
I may do so indeed! One thing though: I think all the errors I
encountered are certainly relatively common and have useless error
messages (when they have a message at all). It would certainly be
relevant to catch these cases in the code and output a configuration
error or something similar... I can open a ticket for that if you think
it is relevant.
> Ah yes, it is because since you are using a session-less container,
> you should disable session reading from the pac4jprofile annotation:
> @Pac4JProfile(readFromSession=false)
I think I want that (as I'd like to avoid using sessions at all if
possible), but I'm not really sure where to add the annotation... I've
tried to add it in MyAuthResource:
https://github.com/BuddhistDigitalResourceCenter/auth-api/commit/970348e374330775c15ff7502e2a3ffd32d9ee06
--
You received this message because you are subscribed to the Google Groups "Pac4j users mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pac4j-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
To unsubscribe from this group and stop receiving emails from it, send an email to pac4j-users...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to pac4j-users+unsubscribe@googlegroups.com.
Hi,
I'm not sure to follow you completely on the JWT session. It's true we don't have a JwtSessionStore, but where would you save the generated JWT? In a cookie?
If you want to generate a JWT, indeed, you need to manually call the JwtGenerator and return it to the caller (in JSON, XML...): I guess you expected something automatic here?
To unsubscribe from this group and stop receiving emails from it, send an email to pac4j-users+unsubscribe@googlegroups.com.
JwtGenerator). You should thus use the @Pac4JSecurity with the name of the client passed as a parameter to protect the endpoint in your resource class.