diff --git a/buildSrc/src/main/kotlin/com.workos.java.examples.java-application-conventions.gradle.kts b/buildSrc/src/main/kotlin/com.workos.java.examples.java-application-conventions.gradle.kts index 5010676..bba686a 100644 --- a/buildSrc/src/main/kotlin/com.workos.java.examples.java-application-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/com.workos.java.examples.java-application-conventions.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation("gg.jte:jte:1.12.0") - implementation("com.workos:workos:2.9.1") + implementation("com.workos:workos:3.1.0-beta.user-management1") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.0") diff --git a/java-usermanagement-example/.settings/org.eclipse.buildship.core.prefs b/java-usermanagement-example/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..b1886ad --- /dev/null +++ b/java-usermanagement-example/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir=.. +eclipse.preferences.version=1 diff --git a/java-usermanagement-example/README.md b/java-usermanagement-example/README.md new file mode 100644 index 0000000..18eb04d --- /dev/null +++ b/java-usermanagement-example/README.md @@ -0,0 +1,78 @@ +If your SaaS product’s backend is built with Java—or a JVM-compatible language such as Kotlin, Groovy, Scala, or Clojure—and you want to incorporate WorkOS’ User Management functionality, you can do a dry-run using this example Java app. It makes use of the [WorkOS Kotlin SDK](https://github.com/workos/workos-kotlin). + +If you get stuck while following the steps below and aren't able to resolve the issue by reading our [API reference](https://workos.com/docs/reference) or [User Management Setup Guide](https://workos.com/docs/user-management/guide), please reach out to us at support@workos.com so we can help! + +## Prerequisites +A free WorkOS account, and each of these installed on your machine: +- Java version 1.8+ +- The Java Development Kit (JDK), which includes the Java Runtime Environment (JRE) + +## Clone the Java app +1. In your CLI, navigate to the directory into which you want to clone the Java example app git repo: +```bash +$ cd ~/Desktop +``` + +2. Clone the main Java example app repo: +```bash +# HTTPS +$ git clone https://github.com/workos/java-example-applications.git + +or + +# SSH +$ git clone git@github.com:workos/java-example-applications.git +``` + +3. Navigate to the cloned repo: +```bash +$ cd java-example-applications/ +``` + +## Securely store the environment variables +4. Obtain and make note of your WorkOS API key and WorkOS Client ID from the WorkOS Dashboard. The locations of these values are shown in the screenshots below. + +![Screenshot of the WorkOS dashboard showing where to locate the API key](https://assets-global.website-files.com/5f03ef1d331a69193fae6dcd/61986a545cae6987e741c044_TXlyTFBXjAfHZwhb9l-YRvpdj3LCCSXX5frveCFXh1Ywlc482yvdpKHDDRl9QKH3CXbsCwCj9Sya4DAmxvvK293sREyeTJJW8NidhsDgc5lXSU15H6cFpHIlXaAeqHXge259YQju.png) + +![Screenshot of the WorkOS dashboard showing where to locate the Client ID](https://assets-global.website-files.com/5f03ef1d331a69193fae6dcd/61986a53882d3a558ae819ee_-ZbW48EgfBtiMuTQEDAaV0UtSxw2wt6Mx-NAX5YxIdI87AZT3bI5w_7jS6tHk-TlG0aHC08AD-l_wr3v_RmUMzSyTehrLIk8D5A7hQ5UskvPVeuXec-9yf6pLTBxkm68PF3kHsqv.png) + +5. Create a .env file in the java-example-applications/ directory to store the environment variables: +```bash +$ touch .env +``` + +6. Open the new .env file with your preferred text editor and replace the placeholder values for WORKOS_API_KEY and WORKOS_CLIENT_ID. +```bash +WORKOS_API_KEY=your_api_key_here +WORKOS_CLIENT_ID=your_project_id_here +``` + +The .env file is listed in this repo's .gitignore file, so your sensitive information will not be checked into version control. This is an important consideration for keeping sensitive information such as API keys private. The WorkOS Kotlin SDK will read your API key and Client ID from the .env file. + +## Set up User Management with WorkOS +7. Create an [Organization](https://dashboard.workos.com/organizations) and an [SSO Connection](https://workos.com/docs/user-management/guide/introduction) in the Organization in your WorkOS Dashboard. +8. Copy the Organization ID from the organization that you just set up and add it to the same .env file that you created in step 6. The .env file should now look like this: +```bash +WORKOS_API_KEY=your_api_key_here +WORKOS_CLIENT_ID=your_project_id_here +WORKOS_ORGANIZATION_ID=your_organization_id_here +``` + +10. Add http://localhost:7001/callback as the default Redirect URI in the Configuration section of the WorkOS Dashboard: + +![A screenshot of te default Redirect URI in the WorkOS dashboard](https://assets-global.website-files.com/5f03ef1d331a69193fae6dcd/619d4e48d8ad3f0711f3b1e3_Screen%20Shot%202021-11-23%20at%2012.24.34%20PM.png) + +## Test the integration +9. Start the server by running this command in the CLI while you’re in the java-example-applications/ directory: +```bash +$ ./gradlew :java-usermanagement-example:run +``` + +10. Navigate to http://localhost:7001. Click the “Login” button. You’ll be prompted to sign in using the identity provider (IdP) you configured during the setup. + +11. Once you’re signed in, the webpage will display the user information for the logged-in user. + +Nice work! You just set up User Management for your Java app! + +## Need help? +If you get stuck while following the steps below and aren't able to resolve the issue by reading our [API reference](https://workos.com/docs/reference) or [User Management Setup Guide](https://workos.com/docs/user-management/guide), please reach out to us at support@workos.com so we can help! diff --git a/java-usermanagement-example/build.gradle.kts b/java-usermanagement-example/build.gradle.kts new file mode 100644 index 0000000..b5df010 --- /dev/null +++ b/java-usermanagement-example/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("com.workos.java.examples.java-application-conventions") +} + +repositories { + flatDir { + dirs("libs") + dirs("build/libs") + } +} + + +application { + mainClass.set("com.workos.java.examples.UserManagementApp") +} diff --git a/java-usermanagement-example/src/main/java/com/workos/java/examples/UserManagementApp.java b/java-usermanagement-example/src/main/java/com/workos/java/examples/UserManagementApp.java new file mode 100644 index 0000000..4c8a17c --- /dev/null +++ b/java-usermanagement-example/src/main/java/com/workos/java/examples/UserManagementApp.java @@ -0,0 +1,76 @@ +package com.workos.java.examples; + +import com.workos.WorkOS; +import com.workos.usermanagement.models.Identity; +import com.workos.usermanagement.models.User; +import com.workos.usermanagement.types.UserManagementProviderEnumType; +import io.github.cdimascio.dotenv.Dotenv; +import io.javalin.Javalin; +import io.javalin.http.Context; +import io.javalin.http.staticfiles.Location; +import java.util.Map; + + +public class UserManagementApp { + private final WorkOS workos; + private final String clientId; + + public UserManagementApp() { + Dotenv env = Dotenv.configure().directory("../.env").load(); + + Javalin app = Javalin.create(config -> { + config.addStaticFiles("src/resources", Location.EXTERNAL); + }).start(7001); + + workos = new WorkOS(env.get("WORKOS_API_KEY")); + clientId = env.get("WORKOS_CLIENT_ID"); + + app.get("/", this::isLoggedIn); + app.post("/login", this::login); + app.get("/callback", this::callback); + app.get("logout", this::logout); + } + + public void login(Context ctx) { + String loginMethod = ctx.formParam("login_method"); + + String url = + workos + .userManagement + .getAuthorizationUrl(clientId, "http://localhost:7001/callback") + .provider(UserManagementProviderEnumType.valueOf(loginMethod)) + .build(); + + ctx.redirect(url); + } + + public void callback(Context ctx) { + String code = ctx.queryParam("code"); + + assert code != null; + + User user = workos.userManagement.authenticateWithCode(clientId, code, null).getUser(); + Identity[] identities = workos.userManagement.getUserIdentities(user.getId()); + ctx.sessionAttribute("user", user); + ctx.sessionAttribute("identities", identities); + + ctx.redirect("/"); + } + + public void isLoggedIn(Context ctx) { + if (ctx.sessionAttribute("user") != null){ + ctx.render("user.jte", Map.of("user", ctx.sessionAttribute("user"), "identities", ctx.sessionAttribute("identities"))); + } else { + ctx.render("home.jte"); + } + } + + public void logout(Context ctx ) { + ctx.sessionAttribute("user", null); + ctx.redirect("/"); + } + + public static void main(String[] args) { + new UserManagementApp(); + } +} diff --git a/java-usermanagement-example/src/main/jte/home.jte b/java-usermanagement-example/src/main/jte/home.jte new file mode 100644 index 0000000..da8423f --- /dev/null +++ b/java-usermanagement-example/src/main/jte/home.jte @@ -0,0 +1,43 @@ + + + +
+
+
+ workos logo +
+ +
+
+ + + + +
+
+
+
+
+
+
+
+ Log in +
+
+ + + +
+
+
+
+
+ diff --git a/java-usermanagement-example/src/main/jte/user.jte b/java-usermanagement-example/src/main/jte/user.jte new file mode 100644 index 0000000..a9e5af7 --- /dev/null +++ b/java-usermanagement-example/src/main/jte/user.jte @@ -0,0 +1,54 @@ +@param com.workos.usermanagement.models.User user +@param com.workos.usermanagement.models.Identity[] identities + + + + +
+
+
+ workos logo +
+ +
+
+ + + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
User
First Name: ${user.getFirstName()}
Last Name: ${user.getLastName()}
Email: ${user.getEmail()}
ID: ${user.getId()}
Email Verified: ${user.getEmailVerified()}
Identities: ${java.util.Arrays.stream(identities).map(i -> i.toString()).toList().toString()}
+
+ +
+
+
+
diff --git a/java-usermanagement-example/src/resources/images/google-button.png b/java-usermanagement-example/src/resources/images/google-button.png new file mode 100644 index 0000000..0332ec0 Binary files /dev/null and b/java-usermanagement-example/src/resources/images/google-button.png differ diff --git a/java-usermanagement-example/src/resources/images/microsoft-button.png b/java-usermanagement-example/src/resources/images/microsoft-button.png new file mode 100644 index 0000000..2c725dc Binary files /dev/null and b/java-usermanagement-example/src/resources/images/microsoft-button.png differ diff --git a/java-usermanagement-example/src/resources/images/saml-button.png b/java-usermanagement-example/src/resources/images/saml-button.png new file mode 100644 index 0000000..e03328f Binary files /dev/null and b/java-usermanagement-example/src/resources/images/saml-button.png differ diff --git a/java-usermanagement-example/src/resources/images/workos-logo-with-text.png b/java-usermanagement-example/src/resources/images/workos-logo-with-text.png new file mode 100644 index 0000000..618f259 Binary files /dev/null and b/java-usermanagement-example/src/resources/images/workos-logo-with-text.png differ diff --git a/java-usermanagement-example/src/resources/images/workos_logo.png b/java-usermanagement-example/src/resources/images/workos_logo.png new file mode 100644 index 0000000..896af10 Binary files /dev/null and b/java-usermanagement-example/src/resources/images/workos_logo.png differ diff --git a/java-usermanagement-example/src/resources/main.css b/java-usermanagement-example/src/resources/main.css new file mode 100644 index 0000000..2385363 --- /dev/null +++ b/java-usermanagement-example/src/resources/main.css @@ -0,0 +1,415 @@ +body { + font-family: Inter, sans-serif; + background-color: #f9f9fb; +} + +.container_login { + display: flex; + flex-direction: column; + justify-content: center; + margin: auto; + width: 30%; + height: 90vh; +} + +.container_login img { + height: 150px; +} + +.container_login h1 { + font-size: 65px; + color: #111111; + position: relative; + bottom: 10px; +} + +.flex { + display: flex; + justify-content: center; + align-items: center; +} + +.flex_column { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.m-top-20 { + margin-top: 20px; +} + +.width-75 { + width: 75%; +} + +.width-40vw { + width: 40vw; +} + +.width-25vw { + width: 25vw; +} + +.width-18vw { + width: 18vw; +} + +.width-941px { + width: 941px; +} + +.width-335 { + width: 335px; +} + +.height-315 { + height: 315px; +} + +.height-40vh { + height: 40vw; +} + +.height-80vh { + height: 80vh; +} + +.height-100vh { + height: 100vh; +} + +.space-between { + justify-content: space-between; +} + +.container_success { + display: flex; + flex-direction: column; + justify-content: center; + margin: auto; + width: 100%; +} + +.heading_div { + margin: 35px 0px 0px 0px; +} + +.heading_text_div { + align-self: center; +} + +.container_success { + display: flex; + flex-direction: column; + justify-content: center; + margin: auto; + width: 100%; + background-size: cover; +} + +.text_input { + border: 1px solid #555555; + border-radius: 10px; + margin: 10px 0px 7px 0px; + padding: 5px; + height: 35px; + width: 18vw; + text-align: center; +} + +.code-input { + width: 75px; + height: 100px; + margin: 0px 5px 30px 5px; + font-size: 60px; + color: darkslategray; +} + +.qr_div { + align-self: center; + margin-top: 45px; +} + +.qr_code { + width: 7vw; + max-width: 100px; +} + +.factor_card { + border: 1px solid #555555; + border-radius: 10px; + width: 20vw; + margin: 0px 15px 0px 15px; + padding: 25px; +} + +.card { + border: 1px solid #555555; + border-radius: 10px; + margin: 0px 15px 0px 15px; + padding: 25px 50px; + margin-bottom: 20px; +} + +.profile_card { + width: 40vw; + overflow: scroll; + background: white; + box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); +} + +.button { + background-color: #6363f1; + border: 2px solid #6363f1; + border-radius: 10px; + color: white; + padding: 8px 16px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + transition-duration: 0.4s; + cursor: pointer; + box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); +} + +.button-outline { + background-color: #f9f9fb; + color: #6363f1; + padding: 8px 16px; +} + +.button-sm { + padding: 8px 16px; +} + +.button:hover, +.button-outline:hover { + background-color: #555555; + border: 2px solid #555555; + color: white; +} + +.sales-button { + margin-left: 10px; +} + +.login_button { + width: 100%; + box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); + background-color: white; + color: ; +} + +.login_button:hover { + border-color: #6363f1; + color: #292929; +} + +h2, +h1 { + text-align: center; + color: #555555; +} + +.logged_in_div_right { + width: 60%; + height: 90vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + bottom: 10%; +} + +.logged_in_div_left { + width: 40%; + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: left; + background-color: #f9f9fb; + margin-left: 4vw; +} + +.logged_in_div_left div { + justify-content: left; + position: relative; + bottom: 20%; +} + +.logged_in_div_left h1 { + color: #111111; + font-size: 75px; + text-align: left; + margin-bottom: 0px; + font-weight: normal; + letter-spacing: -0.05em; +} + +.home-hero-gradient { + background-image: linear-gradient( + 45deg, + #a163f1, + #6363f1 22%, + #3498ea 40%, + #40dfa3 67%, + rgba(64, 223, 163, 0) + ); + background-size: 150% 100%; + background-repeat: no-repeat; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: intro-gradient 1.2s cubic-bezier(0.85, 0.26, 0.89, 0.93); + animation-iteration-count: 1; + animation-fill-mode: backwards; + animation-delay: 0.4s; + text-align: left; + font-size: 75px; + letter-spacing: -0.05em; + font-weight: normal; + margin-top: 0px; +} + +.title-text { + margin-bottom: -50px; +} + +.title-subtext { + color: gray; + line-height: 10px; + margin-bottom: 15px; + font-weight: 200; +} + +.logged_in_div_left button { + padding: 8px 22px; + box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); +} + +.logged_in_nav { + display: flex; + justify-content: space-between; + background-color: #f9f9fb; + padding: 15px 30px 15px 30px; + z-index: 1000; +} + +.logged_in_nav p { + padding: 4px 0px 0px 15px; + line-height: 1; + color: #29363d; +} + +.logged_in_nav img { + height: 50px; +} + +.nav-item { + color: black; + border: 2px solid #f9f9fb; + background-color: #f9f9fb; + box-shadow: none; + border-radius: 5px; +} + +.blog-nav-button { + margin-right: 20px; + background-color: #f9f9fb; + border: 2px solid #f9f9fb; +} + +.nav-item:hover { + background-color: #f9f9fb; + border: 2px solid #f9f9fb; + color: #a6a4a4; +} + +.workos-logo { + position: relative; + top: 38px; + padding-right: 25px; + height: 75px !important; +} + +.webhooks_container { + width: 45vw; + padding: 25px; + max-height: 450px; + overflow-y: scroll; +} + +.mb-0 { + margin-bottom: 0px; +} + +.mb-20 { + margin-botton: 20px; +} + +.google_button { + background-image: url("./images/google-button.png"); + background-size: cover; +} + +.microsoft_button { + background-image: url("./images/microsoft-button.png"); + background-size: cover; +} + +.saml_button { + background-image: url("./images/saml-button.png"); + background-size: cover; +} + +.error_message { + color: #6363f1; + margin-top: 0px; + font-size: 12px; +} + +#noborder { + border: none; +} + +#noborder > :first-child { + display: none; +} + +tr { + text-align: left; + border-bottom: 1px solid #ebebf2; +} + +tr:hover { + background-color: #ebebf2; +} + +table, +td { + padding: 5px 20px; +} + +table { + border-collapse: collapse; + border-radius: 5px; + overflow: hidden; + margin: 25px 0px; + width: 70vw; +} + +th { + text-align: center; + font-weight: bolder; + font-size: large; + background-color: #e3e3e3; + border-right: none; + border-left: none; + padding: 15px 40px; +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7f5fc1f..76a142c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ include( "java-magic-link-example", "java-mfa-example", "java-sso-example", + "java-usermanagement-example", "java-webhooks-example", "java-audit-logs-example", )