Part 1 : Build an OAuth2 server with CXF
Setup the project
Note: In this post I suppose that you are developing an application to deploy in TomEE. If not, you may need to add CXF JAX-RS libraries.
So first, we suppose that we have a standard JAX-RS project setup (org.apache.tomee:javaee-api:7.0 is enough for that).
Then we will add cxf OAuth2 module in our pom.xml:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-security-oauth2</artifactId>
<version>3.1.6</version>
</dependency>
Since we will deploy JAX-RS endpoint, we will create an Application subclass:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("api")
public class OAuth2Application extends Application {
}
And finally we will create the OAuth2 “token” endpoint, which is the endpoint you hit to get an access token or a refresh token (or more ;)). CXF provides one - AccessTokenService - but to control the scope and format, we will just wrap it in our own endpoint:
@Path("token")
@RequestScoped
public class TokenResource {
private final AccessTokenService delegate = new AccessTokenService();
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response handleTokenRequest(final MultivaluedMap<String, String> params) {
return delegate.handleTokenRequest(params);
}
@Context
public void setMessageContext(final MessageContext context) {
delegate.setMessageContext(context);
}
}
For now this is a pure delegation, but this allows us to:
-
Define the scope on the resource (needs to be request scoped cause of the way the AccessTokenService is implemented)
-
If desired, in handleTokenRequest() we can change the Response entity format if the CXF one doesn’t fit the need (don’t forget to do so for error and normal cases)
-
Our token endpoint is a CDI bean so we can use injections and see that it is handy
Side note: setMessageContext is needed by CXF implementation, so we just give it to the delegate instance.
If you execute this endpoint it will probably fail, because you need to setup the OAuth2 chain and the persistence of tokens.
Implement the storage
The storage is handled through org.apache.cxf.rs.security.oauth2.provider.OAuthDataProvider API in CXF OAuth2 extension so you need to implement it and set it on the delegate service.
Let’s take the simple case of a JPA implementation - but a JCache one can be neat as it gives you eviction for free and a faster re-read access ;).
To do so, we need a Token and Client entities:
@Entity
public class ClientEntity {
@Id
private String id;
private String secret;
private boolean confidential;
private String application;
private String webUri;
// getters/setters
}
@Entity
public class TokenEntity {
@Id
private String id;
@ManyToOne
private ClientEntity client;
private String tokenType;
private String refreshToken;
private long expiresIn;
private long issuedAt;
private String issuer;
private String grantType;
private String clientCodeVerifier;
private String nonce;
private String responseType;
private String grantCode;
// getters/setters
}
Then once this is done you have to implement the “repository” which is the OAuthDataProvider. To do that, you can extend AbstractOAuthDataProvider which makes it quite simpler. Finally, since we can use CDI, we will inject our EntityManager and use @Transactional in this repository:
@Transactional
@ApplicationScoped
public class JPAOAuthDataProvider extends AbstractOAuthDataProvider {
@PersistenceContext
private EntityManager em;
@Override
protected boolean isRefreshTokenSupported(final List<String> theScopes) {
return true;
}
// all the code at https://github.com/rmannibucau/simple-oauth2/blob/master/src/main/java/com/github/rmannibucau/oauth2/backend/JPAOAuthDataProvider.java
}
This is a pure CRUD implementation exception isRefreshTokenSupported() which is overriden cause if you don’t do the token endpoint will not generate a refresh token (the alternative is to ensure you request and have the scope refreshToken but this doesn’t work using password grant).
Defining oauth2 chain
Now we have our storage, we need to wire it to the oauth2 server:
@Path("token")
@RequestScoped
public class TokenResource {
private final AccessTokenService delegate = new AccessTokenService();
@Inject
private JPAOAuthDataProvider provider;
@PostConstruct
private void setup() {
delegate.setDataProvider(provider);
// TODO
}
// as before
}
This is a good step forward and quite easy as we rely on CDI. However, we still need to help our delegate to handle the requests it can receive.
For this post, we will handle password and refresh grants (i.e. from a user/password, you get an access_token and refresh_token : the access_token will be your “secret” until it expires and the refresh_token will allow you to get a new one when needed).
CXF provides a set of AccessTokenGrantHandler already implementing main flows/grants. The handler for the password grant is called ResourceOwnerGrantHandler and the one for the refresh is called RefreshTokenGrantHandler. All you need to do is to initialize them if needed and add them to the delegate token service.
In our case, the initialization is simple:
-
Both need the OAuthDataProvider (already injected so no issue)
-
The resource owner grant handler needs a login handler validating credentials. Here again, CXF does all the job providing a JAAS implementation. All we will need to do is to set the JAAS context name (if you don’t know what it is, you will understand it next part)
Finally, here is how we initialize our token service:
@PostConstruct
private void setup() {
delegate.setDataProvider(provider);
Stream.of(new ResourceOwnerGrantHandler() {{
setDataProvider(provider);
setLoginHandler(new JAASResourceOwnerLoginHandler() {{
setContextName("oauth2");
}});
}}, new RefreshTokenGrantHandler() {{
setDataProvider(provider);
}}
).forEach(delegate::setGrantHandler);
}
At that point the token endpoint is done but we still need to setup JAAS to have a working authentication.
JAAS setup
To setup JAAS we need to define a context matching the context name we set on the login handler. In the previous part, we called it “oauth2” so we need to define in jaas.config:
oauth2 {
// login modules
};
To use TomEE properties login module (user, password and groups are in properties files), the JAAS configuration file can look like:
oauth2 {
org.apache.openejb.core.security.jaas.PropertiesLoginModule required
UsersFile="users.properties"
GroupsFile="groups.properties";
};
Finally, to run it, you will need to set the system property java.security.auth.login.config to the path to jaas.config. For instance:
-Djava.security.oauth.login.config=/opt/app/security/jaas.config
Side note: you can also use Tomcat context.xml to configure the JAAS configuration location using JAASRealm directly from the web application.
Next post we will see how to test this oauth2 server and how to package it as a fatjar.
From the same author:
In the same category:

