Skip to content

Instantly share code, notes, and snippets.

@aaronanderson
Last active November 7, 2017 13:51
Show Gist options
  • Save aaronanderson/deda33e2bbd7bd3189fff55b7c3e0a28 to your computer and use it in GitHub Desktop.
Save aaronanderson/deda33e2bbd7bd3189fff55b7c3e0a28 to your computer and use it in GitHub Desktop.
Sample code for OAuth2 password authentication with only JAX-RS
@ApplicationScoped
public class OIDCClient {
private final AtomicReference<String> refreshRef = new AtomicReference<>();
private final ReadWriteLock tokenLock = new ReentrantReadWriteLock();
private OIDCToken oidcToken;
private String refreshToken;
@Inject
@ConfigurationValue("oidc.idpURL")
private String idpURL;
@PostConstruct
public void init() {
client = buildClient();
}
public String getCurrentRefreshToken() throws IOException {
if (refreshToken == null) {
try {
String newRefreshToken = getRefreshToken();
if (refreshRef.compareAndSet(null, newRefreshToken)) {
refreshToken = newRefreshToken;
}
} catch (IOException e) {
logger.log(Level.SEVERE, "Workspace Security Setup error", e);
}
}
return refreshToken;
}
public String getRefreshToken() throws IOException {
/*EntityManager em = null;
try {
em = getEntityManagerFactory().createEntityManager();
RefreshToken token = em.find(RefreshToken.class, 1l);
if (token != null) {
return token.getToken();
}
} catch (Exception e) {
logger.log(Level.SEVERE, " token error", e);
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}*/
return null;
}
public void setRefreshToken(String token) throws IOException {
/*EntityManager em = null;
try {
em = getEntityManagerFactory().createEntityManager();
em.getTransaction().begin();
RefreshToken rToken = new RefreshToken().withId(1l).withToken(token);
em.merge(rToken);
em.getTransaction().commit();
} catch (Exception e) {
logger.log(Level.SEVERE, " token error", e);
em.getTransaction().rollback();
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}*/
}
public String getAccessToken() {
if (refreshToken == null) {
try {
refreshToken = getCurrentRefreshToken();
} catch (IOException e) {
logger.log(Level.SEVERE, "auth setup error", e);
}
}
if ((oidcToken == null || LocalDateTime.now().isAfter(oidcToken.timeoutTime.minusSeconds(30))) && refreshToken != null) {
tokenLock.writeLock().lock();
try {
if ((oidcToken == null || LocalDateTime.now().isAfter(oidcToken.timeoutTime.minusSeconds(30))) && refreshToken != null) {
oidcToken = retrieveAccessTokenSimple().orElse(null);
}
} finally {
tokenLock.writeLock().unlock();
}
}
tokenLock.readLock().lock();
try {
return oidcToken != null ? oidcToken.token : null;
} finally {
tokenLock.readLock().unlock();
}
}
// OAuth redirect is used now instead of native authentication.
public boolean testAuthenticate(String account, String password) {
Optional<JsonObject> result = oidcAuthenticate(account, password, "openid");
return result.isPresent() && result.get().containsKey("access_token");
}
public Optional<OIDCToken> retrieveAccessTokenSimple() {
Client oauthClient = ClientBuilder.newBuilder().build();
try {
oauthClient.register(new OIDCAuthenticator(OIDCAuthenticator.Mode.Basic, () -> String.format("%s:%s", clientId, clientSecret)));
Form form = new Form();
form.param("grant_type", "refresh_token");
form.param("refresh_token", getCurrentRefreshToken());
form.param("scope", "openid profile email groups offline_access");
Response response = oauthClient.target(idpURL).path("/oauth2/v1/token").request().post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
if (response.getStatus() == Response.Status.OK.getStatusCode()) {
JsonObject payload = response.readEntity(JsonObject.class);
String token = payload.getString("access_token");
long expiry = payload.getJsonNumber("expires_in").longValue();
return Optional.of(new OIDCToken(token, LocalDateTime.now().plusSeconds(expiry)));
} else {
logger.log(Level.SEVERE, String.format("Invalid OIDC response %d %s", response.getStatus(), response.readEntity(String.class)));
}
} catch (Throwable t) {
logger.log(Level.SEVERE, "OIDC ", t);
} finally {
oauthClient.close();
}
return Optional.empty();
}
public boolean retrieveTokens(String account, String password) {
Optional<JsonObject> result = oidcAuthenticate(account, password, "openid profile email groups offline_access");
if (result.isPresent() && result.get().containsKey("refresh_token")) {
refreshToken = result.get().getString("refresh_token");
try {
setRefreshToken(refreshToken);
} catch (Throwable t) {
logger.log(Level.SEVERE, "", t);
}
if (result.get().containsKey("access_token")) {
tokenLock.writeLock().lock();
try {
String token = result.get().getString("access_token");
long expiry = result.get().getJsonNumber("expires_in").longValue();
oidcToken = new OIDCToken(token, LocalDateTime.now().plusSeconds(expiry));
} finally {
tokenLock.writeLock().unlock();
}
}
return true;
}
return false;
}
public Optional<JsonObject> oidcAuthenticate(String account, String password, String scope) {
Client oauthClient = ClientBuilder.newBuilder().build();
try {
oauthClient.register(new OIDCAuthenticator(OIDCAuthenticator.Mode.Basic, () -> String.format("%s:%s", clientId, clientSecret)));
Form form = new Form();
form.param("grant_type", "password");
form.param("username", account);
form.param("password", password);
form.param("scope", scope);
Response response = oauthClient.target(idpURL).path("/oauth2/v1/token").request().post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
if (response.getStatus() == Response.Status.OK.getStatusCode()) {
return Optional.of(response.readEntity(JsonObject.class));
} else {
logger.log(Level.SEVERE, String.format("Invalid OIDC response %d %s", response.getStatus(), response.readEntity(String.class)));
}
} catch (Throwable t) {
logger.log(Level.SEVERE, "OIDC ", t);
} finally {
oauthClient.close();
}
return Optional.empty();
}
@Provider
public static class OIDCAuthenticator implements ClientRequestFilter {
private final Mode mode;
private final Supplier<String> tokenGenerator;
public OIDCAuthenticator(Mode mode, Supplier<String> tokenGenerator) {
this.mode = mode;
this.tokenGenerator = tokenGenerator;
}
public void filter(ClientRequestContext requestContext) throws IOException {
try {
String token = tokenGenerator.get();
if (token == null || token.isEmpty()) {
throw new IOException("Empty auth token");
}
final String authorization = mode + " " + (mode == Mode.Basic ? Base64.getEncoder().encodeToString(token.getBytes()) : token);
MultivaluedMap<String, Object> headers = requestContext.getHeaders();
headers.add("Authorization", authorization);
} catch (IllegalArgumentException ex) {
throw new IOException(ex);
}
}
public static enum Mode {
Basic, Bearer;
}
}
public static class OIDCToken {
final String token;
final LocalDateTime timeoutTime;
public OIDCToken(String token, LocalDateTime timeoutTime) {
this.token = token;
this.timeoutTime = timeoutTime;
}
public String getToken() {
return token;
}
}
public boolean isDevStage() {
return false;//return ProjectStage.Development.equals(getProjectStage());
}
public boolean oauthRequired() {
if (!isDevStage()) {
return true;
}
}
private Client buildClient() {
ResteasyClientBuilder builder = new ResteasyClientBuilder();
if (isDevStage()) {
builder.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY);
builder.disableTrustManager();
} else {
try {
SSLContext sslContext = getSSLContext();
builder.sslContext(sslContext);
} catch (Throwable t) {
logger.log(Level.SEVERE, "JAX-RS client TLS setup error ", t);
return null;
}
}
Client client = builder.connectionPoolSize(200).build();
// Client client = ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier(hostnameVerifier).build();
// do not enable OIDC authentication for the development environment
if (oauthRequired()) {
OIDCAuthenticator auth = new OIDCAuthenticator(OIDCAuthenticator.Mode.Bearer, this::getAccessToken);
return client.register(auth);
}
return client;
}
private static SSLContext sslContext;
public static synchronized SSLContext getSSLContext() throws Exception {
if (sslContext == null) {
InputStream fis = Thread.currentThread().getContextClassLoader().getResourceAsStream("oauthclient.jks");// "swarm.cer"
// CertificateFactory cf = CertificateFactory.getInstance("X.509");
// Certificate cert = cf.generateCertificate(fis);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// keyStore.load(null, null);
keyStore.load(fis, "XXXXXXX".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// keyStore.setCertificateEntry("OAUTHCert", cert);
tmf.init(keyStore);
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
}
return sslContext;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment