Hi Charles, hope you don't mind that I moved this discussion to the
tapestry users list as it may be useful for others as well. Yes, I'm right
there with you. The trick to this is to override the SubjectFactory. Shiro
creates a subject for all requests, authenticated or not. The principle is
roughly the same as with the tynamo-federatedaccounts-rollingtokens module,
i.e. you want to create an authenticated subject based on the request
headers. So for JWT, it's something like this:

public class JwtAwareWebSubjectFactory extends DefaultWebSubjectFactory {
    private static final String AUTHORIZATIONHEADER_PREFIX = "Bearer ";

    private HttpServletRequest request;
    private HttpServletResponse response;

    public JwtAwareWebSubjectFactory(HttpServletRequest request,
HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }

    @Override
    public Subject createSubject(SubjectContext context) {
        String authorizationHeader = request.getHeader("Authorization");

        if (authorizationHeader == null) return
super.createSubject(context);

        // create subject for REST requests. SubjectContext doesn't contain
much useful information, we need to read
        // it from JWT token claims instead

        if (!authorizationHeader.contains(AUTHORIZATIONHEADER_PREFIX)) {
throw new IllegalArgumentException(
            "JWT authorization header exist but did not start with '" +
AUTHORIZATIONHEADER_PREFIX + "'"); }

        authorizationHeader =
authorizationHeader.substring(authorizationHeader.indexOf(AUTHORIZATIONHEADER_PREFIX)
            + AUTHORIZATIONHEADER_PREFIX.length());

        Claims claims =
Jwts.parser().setSigningKey(AuthResourceImpl.SECRET.getBytes(StandardCharsets.UTF_8))
            .parseClaimsJws(authorizationHeader).getBody();

        WebSubjectContext wsc = (WebSubjectContext) context;
        SecurityManager securityManager = wsc.resolveSecurityManager();
        Session session = null; // wsc.resolveSession();

request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, false);
        boolean sessionEnabled = false;
        SimplePrincipalCollection principals = new
SimplePrincipalCollection();
principals.add(claims.getSubject(), UserRealm.LOCALACCOUNTS_REALM);
// Somewhat abused JWT's API here, storing user id in audience claim for
convenience
        principals.add(Long.parseLong(claims.getAudience()),
UserRealm.LOCALACCOUNTS_REALM);
        boolean authenticated = true;
        String host = wsc.resolveHost();
        ServletRequest request = wsc.resolveServletRequest();
        ServletResponse response = wsc.resolveServletResponse();

        // we are going backwards here - we set the values to
subjectcontext after creating a subject, but there's no
        // SubjectContextFactory in shiro
        context.setPrincipals(principals);
        context.setSessionCreationEnabled(false);
        context.setAuthenticated(true);

        return new WebDelegatingSubject(principals, authenticated, host,
session, sessionEnabled, request, response,
            securityManager);
    }
}

In your AppModule, the usual:
    @Contribute(ServiceOverride.class)
    public static void overrideServices(MappedConfiguration<Class<?>,
Object> configuration,
        @Local SubjectFactory jwtAwareWebSubjectFactory) {
        configuration.add(SubjectFactory.class, jwtAwareWebSubjectFactory);
    }

As for authentication, it shouldn't matter much whether you do local
username/password authentication or Oauth with a third-party authorization
provider. At the end of a successful flow, you just return a new bearer
token.

Kalle

On Mon, Dec 21, 2015 at 7:25 AM, Charlouze <m...@charlouze.com> wrote:

> Hello !
>
> I'd like to secure my REST API (which is using tapestry-resteasy) using
> two different system :
>
>    - standard session connexion with a form
>    - oauth with no session and a bearer token each connexion for client
>    apps to retrieve and update data. (except for the token endpoint)
>
> The first one is OK as it is the standard approch that I've been using in
> my app for month. Then, I tried to implement the bearer token authorization
> but I'm not able to do it properly. Maybe you can advise me on where to
> look in order to succeed.
>
> Thanks in advance.
>
> Regards,
> Charles.
>

Reply via email to