Secure an Application with Oracle Identity Cloud Service and OpenID Connect
In this guide, you will use the Graal Development Kit for Micronaut (GDK) to create an application that authenticates users with Oracle Identity Cloud Service and OpenID Connect. OpenID Connect (OIDC) is an identity layer on top of the open-standard OAuth 2.0 protocol that verifies an end user’s identity and obtains basic profile information. The OIDC APIs are easier to work with than OAuth because they use JSON instead of XML.
Prerequisites #
- JDK 17 or higher. See Setting up Your Desktop.
- An Oracle Cloud Infrastructure account. See Setting up Your Cloud Accounts.
- The Oracle Cloud Infrastructure CLI installed with local access configured.
- An Oracle Cloud Infrastructure compartment.
- The GDK CLI. See Setting up Your Desktop. (Optional.)
Follow the steps below to create the application from scratch. However, you can also download the completed example:
A note regarding your development environment
Consider using Visual Studio Code, which provides native support for developing applications with the Graal Development Kit extension.
Note: If you use IntelliJ IDEA, enable annotation processing.
Windows platform: The GDK guides are compatible with Gradle only. Maven support is coming soon.
1. Create the Application #
Create an application using the GDK Launcher.
-
Open the GDK Launcher in advanced mode.
- Create a new project using the following selections.
- Project Type: Application (default)
- Project Name: oci-security-demo
- Base Package: com.example (Default)
- Clouds: OCI
- Build Tool: Gradle (Groovy) or Maven
- Language: Java (Default)
- Test Framework: JUnit (default)
- Java Version: 17 (default)
- Micronaut Version: (default)
- Cloud Services: Security
- Features: GraalVM Native Image (Default)
- Sample Code: Yes (Default)
- Click Generate Project, then click Download Zip. The GDK Launcher creates an application with the default package
com.example
in a directory named oci-security-demo. The application ZIP file will be downloaded to your default downloads directory. Unzip it, open it in your code editor, and proceed to the next steps.
Alternatively, use the GDK CLI as follows:
gdk create-app com.example.oci-security-demo \
--clouds=oci \
--services=security \
--features=graalvm \
--build=gradle \
--jdk=17 \
--lang=java
gdk create-app com.example.oci-security-demo \
--clouds=oci \
--services=security \
--features=graalvm \
--build=maven \
--jdk=17 \
--lang=java
For more information, see Using the GDK CLI.
1.1. AuthController #
The GDK Launcher created a class named AuthController
to handle requests to /
. It displays the email of an authenticated person, if any. The controller endpoint is annotated with a @View
annotation that uses a JTE template. The file named oci/src/main/java/com/example/AuthController.java has the following contents:
package com.example;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.security.annotation.Secured;
import io.micronaut.security.authentication.Authentication;
import io.micronaut.views.View;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static io.micronaut.security.rules.SecurityRule.IS_ANONYMOUS;
import static io.micronaut.security.rules.SecurityRule.IS_AUTHENTICATED;
@Controller // <1>
class AuthController {
@Secured(IS_ANONYMOUS) // <2>
@View("auth") // <3>
@Get // <4>
Map<String, Object> index(@Nullable Authentication authentication) { // <5>
Map<String, Object> model = new HashMap<>();
if (authentication != null) {
model.put("username", authentication.getAttributes().get("sub"));
} else {
model.put("username", "Anonymous");
}
return model;
}
@Secured(IS_AUTHENTICATED) // <6>
@Get("/secure") // <7>
Map<String, Object> secured() {
return Collections.singletonMap("secured", true); // <8>
}
}
1 The class is defined as a controller with the @Controller
annotation mapped to the path /
.
2 Annotate with @Secured
to configure secured access. The SecurityRule.IS_ANONYMOUS
expression allows access without authentication.
3 Use the @View
annotation to specify which template to use to render the response.
4 The @Get
annotation maps the index
method to an HTTP GET request on /
.
5 Micronaut Security will inject the Authentication
instance as a method parameter; by annotating with @Nullable
, you can determine whether the user is authenticated or not, and populate the model map accordingly.
6 Annotate with @Secured
to configure secured access. The SecurityRule.IS_AUTHENTICATED
expression allows access only to authenticated users.
7 The @Get
annotation maps the secured
method to an HTTP GET request on /secure
.
8 This method simply returns a model map that will be rendered as JSON (because there is no @View
annotation).
1.2. JTE Template #
The GDK Launcher created a JTE template in a file named oci/src/main/jte/auth.jte to render the UI for the controller. It has the following contents:
@param String username
@param java.util.Map<?, ?> security
<!DOCTYPE html>
<html lang="en">
<head>
<title>GDK - Oracle OpenID Connect</title>
</head>
<body>
<h1>GDK - Oracle OpenID Connect</h1>
<h2>username: <span>${username}</span></h2>
<nav>
<ul>
@if(security == null)
<li><a href="/oauth/login/gdk">Enter</a></li>
@else
<li><a href="/logout">Logout</a></li>
@endif
</ul>
</nav>
</body>
</html>
2. Configure OpenID Connect at Oracle Cloud Infrastructure #
You will use the Oracle Cloud console to create an OAuth 2.0 “Confidential Application” with support for OpenID Connect and a federated user to demonstrate using OpenID Connect with Micronaut® and Oracle Cloud Infrastructure.
2.1. Create a Federated User #
-
Log in to your Oracle Cloud Infrastructure tenancy as an admin (or as a user with sufficient permissions to create users and applications).
-
In the Oracle Cloud Console, open the navigation menu, click Identity & Security. Under Identity, click Federation.
-
Click OracleIdentityCloudService, and then click Create User.
-
Enter a valid username, for example, “gdk_guide_oidc”, and a valid email address, along with a first and last name. Click Create.
-
Open the email sent to the email address you specified and click the link to set the password for the user.
-
Log in as the user to verify the password. Then log out and back in as an admin user.
2.2. Create the OAuth Application #
-
Navigate again to the OracleIdentityCloudService federation and copy the link to the Oracle Identity Cloud Service Console (it will resemble
https://idcs-7084de14ec…….identity.oraclecloud.com/ui/v1/adminconsole
). Save this for later when you create the application: you will need this value to set theOAUTH_ISSUER
. -
Click Oracle Identity Cloud Service Console to open the console.
-
Click the button in the top right of the Applications and Services section to add a new application.
-
Click Confidential Application. You are presented with a Wizard containing five steps.
-
Step 1 of the Wizard: Enter a valid application name, for example, “gdk_guide_oidc_app” and optionally a description. Click Next to go to step 2.
-
Step 2 of the Wizard: Click Configure this application as a client now, and provide the following information:
- Allowed Grant Types: Authorization Code
- Allow non-HTTPS URLs: check
- Redirect URL: http://localhost:8080/oauth/callback/gdk
- Logout URL: http://localhost:8080/logout
- Post Logout Redirect URL: http://localhost:8080
6.1. Scroll down and in the section titled Grant the client access to Identity Cloud Service Admin APIs, click Add.
6.2. Select Me.
-
Click Next, and again click Next to accept the defaults for steps 3, 4, and 5 of the Wizard. Then click Finish.
-
Save the Client ID and Client Secret values for later. Then click Close.
-
Finally, click Activate to make the application available for use.
Note: Select Allow non-HTTPS URLs to make testing easier and avoid having to use HTTPS locally. Be sure to unselect this in production applications, which should always use HTTPS.
2.3. Enable Signing Certificate Access #
Make the signing certificate available to your application for JSON Web Tokens (JWT) validation without being authenticated.
-
In the Oracle Identity Cloud Service console, expand the Navigation Drawer, click Settings, and then click Default Settings.
-
Turn on the switch under Access Signing Certificate to enable clients to access the tenant signing certificate without logging in to Oracle Identity Cloud Service.
-
Click Save to save the default settings.
If you skip these steps, you will see similar errors for valid logins because Micronaut Security cannot retrieve the JSON Web Key (JWK) to validate the JWT:
JWT signature validation failed for provider [gdk]
Exception loading JWK from https://idcs-7084de14ec.......identity.oraclecloud.com/admin/v1/SigningCert/jwk
Server returned HTTP response code: 401
2.4. Set the Issuer URL #
Configure the “issuer” URL for JWT validation to succeed.
-
Return to the menu on the left and click Security and OAuth.
-
Enter the issuer URL you saved (the Oracle Identity Cloud Service console URL without the
/ui/v1/adminconsole
path) in the “Issuer” field, for example,https://idcs-7084de14ec…….identity.oraclecloud.com
.
2.5. Configure the Application #
The GDK Launcher generated an initial security configuration in oci/src/main/resources/application.properties, as follows:
micronaut.application.name=oci
# <1>
micronaut.security.authentication=idtoken
micronaut.security.endpoints.logout.enabled=true
# <2>
micronaut.security.endpoints.logout.get-allowed=true
# <3> <4>
micronaut.security.oauth2.clients.gcn.client-id=${OAUTH_CLIENT_ID\:xxx}
# <3> <4>
micronaut.security.oauth2.clients.gcn.client-secret=${OAUTH_CLIENT_SECRET\:yyy}
# <3> <4>
micronaut.security.oauth2.clients.gcn.openid.issuer=${OAUTH_ISSUER\:zzz}
micronaut.security.token.jwt.signatures.secret.generator.secret=${JWT_GENERATOR_SIGNATURE_SECRET\:pleaseChangeThisSecretForANewOne}
1 Set micronaut.security.authentication
as idtoken
. The idtoken
provided by your OAuth 2.0 application when the Authorization code flow ends (a signed JWT) will be saved in a cookie.
2 Accept GET requests to the /logout
endpoint
3 The provider identifier must match the last part of the URL you entered as a redirect URL: /oauth/callback/gdk
.
4 The client-id
, client-secret
, and the issuer
URL will be set as environment variables.
3. Run the Application #
The generated application.properties file contains placeholders for the client-id
, client-secret
, and the issuer
URL properties. Create environment variables for those before starting the application.
-
Create
OAUTH_CLIENT_ID
,OAUTH_CLIENT_SECRET
, andOAUTH_ISSUER
environment variables. Use the client ID and the client secret you saved when you created the Oracle Cloud Infrastructure application, and the issuer URL you saved (the Oracle Identity Cloud Service console URL without the/ui/v1/adminconsole
path):export OAUTH_CLIENT_ID=XXXXXXXXXX export OAUTH_CLIENT_SECRET=YYYYYYYYYY export OAUTH_ISSUER=https://idcs-7084de14ec…….identity.oraclecloud.com
set OAUTH_CLIENT_ID=XXXXXXXXXX set OAUTH_CLIENT_SECRET=YYYYYYYYYY set OAUTH_ISSUER=https://idcs-7084de14ec…….identity.oraclecloud.com
$ENV:OAUTH_CLIENT_ID = "XXXXXXXXXX" $ENV:OAUTH_CLIENT_SECRET = "YYYYYYYYYY" $ENV:OAUTH_ISSUER = "https://idcs-7084de14ec…….identity.oraclecloud.com"
-
Run the application, using the following command, which starts the application on port 8080.
-
Open http://localhost:8080/secure in a browser, and you will be redirected to the home page (because the controller method is annotated with
@Secured(IS_AUTHENTICATED)
, and you are not yet authenticated): -
Sign in with the username and password for the federated user you created.
-
Click Allow on the next screen.
-
When redirected to the home page, you will see that you are authenticated and now have a Logout link. Several authentication details are also displayed.
-
Navigate again to http://localhost:8080/secure in a browser, and you will see a simple JSON response:
4. Generate a Native Executable Using GraalVM #
The GDK supports compiling Java applications ahead-of-time into native executables using GraalVM Native Image. You can use the Gradle plugin for GraalVM Native Image building/Maven plugin for GraalVM Native Image building. Packaged as a native executable, it significantly reduces application startup time and memory footprint.
Prerequisites: Make sure you have installed a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.
To generate a native executable, run the following command:
./gradlew :oci:nativeCompile
The native executable is created in the oci/build/native/nativeCompile/ directory and can be run with the following command:
oci/build/native/nativeCompile/oci-security-demo-oci
./mvnw install -pl lib -am
./mvnw package -pl oci -Dpackaging=native-image
The native executable is created in the oci/target/ directory and can be run with the following command:
oci/target/oci-security-demo-oci
5. Run and Test the Native Executable #
Run the native executable, and then perform the same tests as in step 3.
Summary #
This guide demonstrated how to use the GDK to create a secure OAuth 2.0 Confidential Application with support for OpenID Connect and a federated user to demonstrate using OpenID Connect with Oracle Cloud Infrastructure. Then you packaged that application into a native executable with GraalVM Native Image.
Related Documentation #
- Micronaut Security.
- OpenID Connect
- GraalVM Native Image
- Check out this blog post on using OpenID Connect with Micronaut and Oracle Cloud Infrastructure