This example app shows how to build a Java EE REST API and secure it with JWT and OIDC.
Please read Build a Java REST API with Java EE and OIDC to see how this app was created.
Prerequisites: Java 8, Maven, and an Okta Developer Account.
Okta has Authentication and User Management APIs that reduce development time with instant-on, scalable user infrastructure. Okta's intuitive API and expert support make it easy for developers to authenticate, manage, and secure users and roles in any application.
You will need to create an OIDC Application in Okta to get your settings to perform authentication.
- Log in to your developer account on developer.okta.com.
- Navigate to Applications and click on Add Application.
- Select Web and click Next.
- Give the application a name (e.g.,
Java EE Secure API
) and add the following as Login redirect URIs:http://localhost:3000/implicit/callback
http://localhost:8080/login/oauth2/code/okta
http://localhost:8080/callback?client_name=OidcClient
- Click Done, then edit the project and enable "Implicit (Hybrid)" as a grant type (allow ID and access tokens) and click Save.
To see how the JWT Verifier for Java works, clone this project and check out the jwt-verifier
branch.
git clone -b jwt-verifier https://github.com/oktadeveloper/okta-java-ee-rest-api-example.git
Then modify src/main/java/com/okta/developer/JwtFilter.java
and replace the issuer and client ID with the values from the app you created.
public void init(FilterConfig filterConfig) {
try {
jwtVerifier = new JwtHelper()
.setIssuerUrl("https://{yourOktaDomain}/oauth2/default")
.setClientId("{yourClientId}")
.build();
} catch (IOException | ParseException e) {
System.err.print("Configuring JWT Verifier failed!");
e.printStackTrace();
}
}
Start the app using mvn clean package tomee:run
.
To prove it works with a valid JWT, you can clone our Bootiful React project, and run its UI:
git clone -b okta https://github.com/oktadeveloper/spring-boot-react-example.git bootiful-react
cd bootiful-react/client
npm install
Edit this project's src/App.tsx
file and change the issuer
and clientId
to match your application.
const config = {
issuer: 'https://{yourOktaDomain}/oauth2/default',
redirectUri: window.location.origin + '/implicit/callback',
clientId: '{yourClientId}'
};
Then start it:
npm start
You should then be able to login at http://localhost:3000
with the credentials you created your account with.
The Spring Security implementation in this project will prompt you to login when you try to access the API, and it will setup a resource server that can serve data to a JavaScript client.
To see Spring Security with Java EE in action, clone this project and check out the spring-security
branch.
git clone -b spring-security https://github.com/oktadeveloper/okta-java-ee-rest-api-example.git
Update src/main/resources/application.properties
and fill it with your Okta OIDC app settings.
okta.client-id={clientId}
okta.client-secret={clientSecret}
okta.issuer-uri=https://{yourOktaDomain}/oauth2/default
Then start the app using mvn clean package tomee:run
.
If you try to access http://localhost:8080
, you'll be redirected to Okta to log in. If you use the aforementioned React client to talk to your API, everything should just work.
The Pac4J implementation in this project is very similar to Spring Security. It'll prompt you to log in when you hit the API directly, or look for an Authorization
header if you talk to it from a JavaScript client.
To see Pac4J with Java EE in action, clone this project and check out the pac4j
branch.
git clone -b pac4j https://github.com/oktadeveloper/okta-java-ee-rest-api-example.git
Update src/main/java/com/okta/developer/SecurityConfigFactory.java
and change the issuer, client ID, and client secret to match your Okta app.
public class SecurityConfigFactory implements ConfigFactory {
private final JwtAuthenticator jwtAuthenticator = new JwtAuthenticator();
private final ObjectMapper mapper = new ObjectMapper();
@Override
public Config build(final Object... parameters) {
System.out.print("Building Security configuration...\n");
final OidcConfiguration oidcConfiguration = new OidcConfiguration();
oidcConfiguration.setClientId("{yourClientId}");
oidcConfiguration.setSecret("{yourClientSecret}");
oidcConfiguration.setDiscoveryURI("https://{yourOktaDomain}/oauth2/default/.well-known/openid-configuration");
oidcConfiguration.setUseNonce(true);
final OidcClient<OidcProfile, OidcConfiguration> oidcClient = new OidcClient<>(oidcConfiguration);
oidcClient.setAuthorizationGenerator((ctx, profile) -> {
profile.addRole("ROLE_USER");
return profile;
});
HeaderClient headerClient = new HeaderClient("Authorization", "Bearer ", (credentials, ctx) -> {
String token = ((TokenCredentials) credentials).getToken();
if (token != null) {
try {
// Get JWK
URL keysUrl = new URL("https://{yourOktaDomain}/oauth2/default/v1/keys");
Map map = mapper.readValue(keysUrl, Map.class);
List keys = (ArrayList) map.get("keys");
String json = mapper.writeValueAsString(keys.get(0));
// Build key pair and validate token
KeyPair rsaKeyPair = JWKHelper.buildRSAKeyPairFromJwk(json);
jwtAuthenticator.addSignatureConfiguration(new RSASignatureConfiguration(rsaKeyPair));
CommonProfile profile = jwtAuthenticator.validateToken(token);
credentials.setUserProfile(profile);
System.out.println("Hello, " + profile.getId());
} catch (IOException e) {
System.err.println("Failed to validate Bearer token: " + e.getMessage());
e.printStackTrace();
}
}
});
final Clients clients = new Clients("http://localhost:8080/callback",
oidcClient, headerClient, new AnonymousClient());
return new Config(clients);
}
}
Start the app using mvn clean package tomee:run
.
If you try to access http://localhost:8080
, you'll be redirected to Okta to log in. If you use the aforementioned React client to talk to your API, everything should just work.
This example uses the following open source libraries:
Please post any questions as comments on the blog post, or visit our Okta Developer Forums. You can also email [email protected] if you'd like to create a support ticket.
Apache 2.0, see LICENSE.