Thanks Tim for the report. Entered an issue and will try to fix it tomorrow for 2.2 M1: https://github.com/restlet/restlet-framework-java/issues/697
Thanks, Jerome -- http://www.restlet.com http://twitter.com/#!/jlouvel 2012/12/10 Tim Peierls <[email protected]> > Here's a better candidate for the default implementation of > CookieAuthenticator.challenge: > > @Override public void challenge(Response response, boolean stale) { > Reference ref = response.getRequest().getResourceRef(); > String redirectQueryName = getRedirectQueryName(); > String redirectQueryValue = > ref.getQueryAsForm().getFirstValue(redirectQueryName, ""); > > if ("".equals(redirectQueryValue)) { > redirectQueryValue = new Reference(getLoginFormPath()) > .addQueryParameter(redirectQueryName, ref.toString()) > .toString(); > } > > response.redirectSeeOther(redirectQueryValue); > } > > If the redirect URI already contains a redirect query value we use it > directly, because it means we're creating a challenge after a failed login > attempt, so by using the redirect query value, we're restarting the whole > process. Otherwise, this is a first login attempt, so we create a new > reference to the login path with a redirect query. > > For example, suppose the user tries to access a resource at /foo that > requires authentication/authorization. Since /foo does not contain a > targetUri query parameter, the if-test succeeds and this will result in a > challenge redirection to /login?targetUri=/foo. If that login attempt > fails, the authentication machinery now tries to authenticate > /login?targetUri=/foo. This time, redirectQueryValue will be /foo, the if > clause will be skipped, and the redirection will be to /foo, back to our > starting point. > > This is slightly brittle because of the possibility that other resources > might use the redirect query name in queries, too, which would confuse this > logic. Consider using a name other than the default "targetUri" if this > worries you. > > --tim > > On Sun, Dec 9, 2012 at 4:44 PM, Tim Peierls <[email protected]> wrote: > >> No one answered the question below, and I'd still like to get an answer, >> but in the meantime I got CookieAuthenticator to work by overriding >> challenge(Response, boolean) as follows. >> >> @Override public void challenge(Response response, boolean stale) { >> response.redirectSeeOther( >> new Reference(getLoginFormPath()) >> .addQueryParameter( >> getRedirectQueryName(), >> response.getRequest().getResourceRef().toString() >> ) >> .toString() // Interpret URI relative to this host. >> ); >> } >> >> This would seem to be a good candidate for a default implementation of >> CookieAuthenticator.challenge(Response, boolean). >> >> Here's the rest of the story: I have an AuthApplication to which /auth >> requests are routed in my main component. >> >> // MainComponent initialization: >> host.attach("/auth", authApplication); >> >> That application routes /loginForm to a login form resource and routes >> everything else to an instance of my CookieAuthenticator extension around a >> Restlet that does nothing. (If you leave out this vacuous Restlet, the >> machinery complains about a Filter with no target.) >> >> // In AuthApplication.createInboundRoot: >> Authenticator authenticator = ... my CookieAuthenticator extension >> ...; >> authenticator.setNext(new Restlet() { >> @Override public void handle(Request request, Response response) { >> getLogger().debug("Null restlet"); >> } >> }); >> >> router.attach("/loginForm", LoginFormServerResource.class); >> router.attachDefault(authenticator); >> >> LoginFormServerResource is a straighforward use of Freemarker's >> TemplateRepresentation: >> >> @Get public Representation showLoginForm() { >> ImmutableMap<String, String> dataModel = ImmutableMap.of( >> "targetUri", getQuery().getFirstValue("targetUri") >> ); >> return new TemplateRepresentation( >> LOGIN_FORM_TEMPLATE, freemarkerConfiguration, >> dataModel, MediaType.TEXT_HTML >> ); >> } >> >> The template has a form element with an action to post to the login >> resource, using the default values of CookieAuthenticator's >> identifierFormName and secretFormName properties ("login", "password"). A >> stripped-down example: >> >> <form action="/auth/login?targetUri=${targetUri}" method="POST"> >> <input type="text" id="login" name="login" size="15" /> >> <input type="password" id="password" name="password" size="15" /> >> <input type="submit" value="Login" /> >> </form> >> >> Because I've made authentication/authorization a separate Application, I >> had to set the cookie path to "/" in my CookieAuthenticator extension in >> order to have the credentials in the cookie apply to the entire URI-space >> of my Restlet Component. >> >> @Override protected CookieSetting getCredentialsCookie(Request >> request, Response response) { >> CookieSetting credentialsCookie = >> super.getCredentialsCookie(request, response); >> credentialsCookie.setPath("/"); >> return credentialsCookie; >> } >> >> --tim >> >> >> On Mon, Dec 3, 2012 at 3:38 PM, Tim Peierls <[email protected]> wrote: >> >>> The class javadoc for CookieAuthenticator says: >>> >>> When the credentials are missing or stale, the challenge(Response, >>>> boolean)<http://www.restlet.org/documentation/2.1/jee/ext/org/restlet/ext/crypto/CookieAuthenticator.html#challenge(org.restlet.Response,%20boolean)> >>>> method >>>> is invoked by the parent class, and its default behavior is to redirect the >>>> user's browser to the >>>> getLoginFormPath()<http://www.restlet.org/documentation/2.1/jee/ext/org/restlet/ext/crypto/CookieAuthenticator.html#getLoginFormPath()> >>>> URI, >>>> adding the URI of the target resource as a query parameter of name >>>> getRedirectQueryName()<http://www.restlet.org/documentation/2.1/jee/ext/org/restlet/ext/crypto/CookieAuthenticator.html#getRedirectQueryName()> >>>> . >>> >>> >>> But the javadoc for CookieAuthenticator.challenge(Response, boolean) >>> says it must be overridden to return a login form representation, and >>> in fact the implementation of challenge(Response, boolean) is to call >>> super.challenge(response, stale), in both stable and unstable versions. The >>> supertype version (ChallengeAuthenticator.challenge) sets the status to >>> unauthorized and creates a challenge request from the challenge scheme and >>> puts it in the response. This fails because the HTTP_Cookie scheme is not >>> meant to be used in this way. >>> >>> My question is whether the class comment is the intent, and the current >>> implementation of CookieAuthenticator.challenge is incomplete, or if the >>> class comment is out of date. If the latter is the case, then can someone >>> give an example of how to override challenge? >>> >>> --tim >>> >> >> > ------------------------------------------------------ http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3037701

