diff --git a/docs/docs/examples/introduction.mdx b/docs/docs/examples/introduction.mdx
index 7ae7ab8924..6587334735 100644
--- a/docs/docs/examples/introduction.mdx
+++ b/docs/docs/examples/introduction.mdx
@@ -101,7 +101,7 @@ Our examples cover a range of programming languages and frameworks, so no matter
Java Spring Boot Web |
|
- |
+ Guide |
|
@@ -194,7 +194,7 @@ Our examples cover a range of programming languages and frameworks, so no matter
Java Spring Boot API |
|
- |
+ Guide |
|
diff --git a/docs/docs/examples/login/java-spring.md b/docs/docs/examples/login/java-spring.md
new file mode 100644
index 0000000000..22a60c06cc
--- /dev/null
+++ b/docs/docs/examples/login/java-spring.md
@@ -0,0 +1,177 @@
+---
+title: ZITADEL with Java Spring Boot
+sidebar_label: Java Spring Boot
+---
+
+This integration guide demonstrates the recommended way to incorporate ZITADEL into your Spring Boot web application.
+It explains how to enable user login in your application and how to fetch data from the user info endpoint.
+
+By the end of this guide, your application will have login functionality and will be able to access the current user's profile.
+
+:::info
+This documentation references our [example](https://github.com/zitadel/zitadel-java) on GitHub.
+You can either create your own application or directly run the example by providing the necessary arguments.
+:::
+
+## Set up application
+
+Before we begin developing our application, we need to perform a few configuration steps in the ZITADEL Console.
+You'll need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your Project, then add a new application at the top of the page.
+Select the **Web** application type and continue.
+
+
+
+We recommend that you use [Proof Key for Code Exchange (PKCE)](/apis/openidoauth/grant-types#proof-key-for-code-exchange) for all applications.
+
+
+
+### Redirect URIs
+
+The Redirect URIs field tells ZITADEL where it's allowed to redirect users after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
+The Post-logout redirect send the users back to a route on your application after they have logged out.
+
+:::info
+If you are following along with the [example](https://github.com/zitadel/zitadel-java), set the dev mode to `true`, the Redirect URIs to `http://localhost:18080/webapp/login/oauth2/code/zitadel` and Post redirect URI to `http://localhost:18080/webapp`.
+:::
+
+
+
+Continue and create the application.
+
+### Client ID
+
+After successful creation of the app, a pop-up will appear displaying the app's client ID. Copy the client ID, as you will need it to configure your Java client.
+
+
+
+## Spring setup
+
+Now that you have configured your web application on the ZITADEL side, you can proceed with the integration of your Spring client.
+This guide will reference the [example repository](https://github.com/zitadel/zitadel-java) and explain the necessary steps taken in there.
+If your starting from scratch, you can use the Spring Initializer with the [following setup](https://start.spring.io/#!type=maven-project&language=java&platformVersion=3.2.1&packaging=jar&jvmVersion=17&dependencies=web,thymeleaf,security,oauth2-client,lombok) as a base.
+
+### Support classes
+
+To be able to take the most out of ZITADELs RBAC, we first need to create a GrantedAuthoritiesMapper, that will map the role claims (`urn:zitadel:iam:org:project:roles`)
+into Spring Security `authiorities`, which can be used later on to determine the granted permissions.
+
+So in your application, create a 'support/zitadel' package and in there the `ZitadelGrantedAuthoritiesMapper.java`:
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/support/zitadel/ZitadelGrantedAuthoritiesMapper.java
+```
+
+The following two classes will provide you the possibility to access and use the user's token for requests to another API, e.g. the Spring Boot API example.
+
+Directly create them in the `support` package.
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/support/TokenAccessor.java
+```
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/support/AccessTokenInterceptor.java
+```
+
+### Application server configuration
+
+As we have now our support classes, we can now create and configure the application server (and API client) itself.
+
+In a new `config` package, create first the `WebClientConfig.java`, which will provide a RestTemplate using the previously created AccessTokenInterceptor:
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/config/WebClientConfig.java
+```
+
+Additionally also create the `WebSecurityConfig.java` in the same package. This class will take care of the authentication, redirecting the user to the login,
+mapping the claims (using the ZitadelGrantedAuthoritiesMapper) and also provide the possibility for logout:
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/config/WebSecurityConfig.java
+```
+
+For the authentication (and the server in general) to work, the application needs some configuration, so please provide the following to your `application.yml` (resources folder):
+
+```yaml reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/application.yml
+```
+
+Note that both the `issuer-uri` as well as the `client-id` are only placeholders. You can either change them in here using the values provided by ZITADEL
+or pass them later on as arguments when starting the application.
+
+### Add pages to your application
+
+To be able to serve these pages create a `templates` directory in the `resources` folder.
+Now create three HTML files in the new `templates` folder and copy the content of the examples:
+
+**index.html**
+
+The home page will display the Userinfo from the authentication context and the granted roles / Spring security authorities.
+
+```html reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/templates/index.html
+```
+
+**fragments.html**
+
+The navigation to switch between the home and tasks page and allows the user to logout.
+
+```html reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/templates/fragments.html
+```
+
+**tasks.html**
+
+The tasks page allows to interact with the Spring Boot API example and display / add new tasks.
+
+```html reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/templates/tasks.html
+```
+
+**UiController**
+
+To serve these pages and handler their actions, you finally need a `UiController.java`:
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/web/UiController.java
+```
+
+### Start your application
+
+In case you've created your own application and depending on your development setup you might need to build the application first:
+
+```bash
+mvn clean package -DskipTests
+```
+
+You will need to provide the `issuer-uri` (your ZITADEL domain) and the `client-id` previously created:
+
+```bash
+java \
+ -Dspring.security.oauth2.client.provider.zitadel.issuer-uri= \
+ -Dspring.security.oauth2.client.registration.zitadel.client-id= \
+ -jar web/target/web-0.0.2-SNAPSHOT.jar
+```
+
+This could look like:
+
+```bash
+java \
+ -Dspring.security.oauth2.client.provider.zitadel.issuer-uri=https://my-domain.zitadel.cloud \
+ -Dspring.security.oauth2.client.registration.zitadel.client-id=243861220627644836@example \
+ -jar web/target/web-0.0.2-SNAPSHOT.jar
+```
+
+If you then visit on you should directly be redirected to your ZITADEL instance.
+After login with your existing user you will be presented the profile page:
+
+
+
+## Completion
+
+Congratulations! You have successfully integrated your Spring Boot web application with ZITADEL!
+
+If you get stuck, consider checking out our [example](https://github.com/zitadel/zitadel-java) application.
+This application includes all the functionalities mentioned in this quickstart.
+You can directly start it with your own configuration. If you face issues, contact us or raise an issue on [GitHub](https://github.com/zitadel/zitadel-java/issues).
+
diff --git a/docs/docs/examples/secure-api/java-spring.md b/docs/docs/examples/secure-api/java-spring.md
new file mode 100644
index 0000000000..c545f13c25
--- /dev/null
+++ b/docs/docs/examples/secure-api/java-spring.md
@@ -0,0 +1,283 @@
+---
+title: ZITADEL with Java Spring Boot
+sidebar_label: Java Spring Boot
+---
+
+This integration guide shows you how to integrate **ZITADEL** into your Java Spring Boot API. It demonstrates how to secure your API using
+OAuth 2 Token Introspection.
+
+At the end of the guide you should have an API with a protected endpoint.
+
+:::info
+This documentation references our [example](https://github.com/zitadel/zitadel-java) on GitHub.
+You can either create your own application or directly run the example by providing the necessary arguments.
+:::
+
+## Set up application
+
+Before we begin developing our API, we need to perform a few configuration steps in the ZITADEL Console.
+You'll need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your Project, then add a new application at the top of the page.
+Select the **API** application type and continue.
+
+
+
+Select Basic Auth for authenticating at the Introspection Endpoint.
+
+
+
+After successful creation of the app, a pop-up will appear displaying the app's client ID. Copy the client ID and secret, as you will need it to configure your Java client.
+
+
+
+## Spring Setup
+
+Now that you have configured your web application on the ZITADEL side, you can proceed with the integration of your Spring client.
+This guide will reference the [example repository](https://github.com/zitadel/zitadel-java) and explain the necessary steps taken in there.
+If your starting from scratch, you can use the Spring Initializer with the [following setup](https://start.spring.io/#!type=maven-project&language=java&platformVersion=3.2.1&packaging=jar&jvmVersion=17&dependencies=web,lombok,oauth2-resource-server) as a base.
+
+### Support class
+
+To be able to take the most out of ZITADELs RBAC, we first need to create a CustomAuthorityOpaqueTokenIntrospector, that will
+customize the introspection behaviour and map the role claims (`urn:zitadel:iam:org:project:roles`)
+into Spring Security `authiorities`, which can be used later on to determine the granted permissions.
+
+So in your application, create a `support/zitadel` package and in there the `CustomAuthorityOpaqueTokenIntrospector.java`:
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/api/src/main/java/demo/app/support/zitadel/CustomAuthorityOpaqueTokenIntrospector.java
+```
+
+### Application server configuration
+
+As we have now our support class, we can now create and configure the application server itself.
+
+In a new `config` package, create the `WebSecurityConfig.java`.
+This class will take care of the authorization by require the calls on `/api/tasks` to be authorized. Any other endpoint will be public by default.
+It will also use the just created CustomAuthorityOpaqueTokenIntrospector for the introspection call:
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/api/src/main/java/demo/app/config/WebSecurityConfig.java
+```
+
+For the authorization (and the server in general) to work, the application needs some configuration, so please provide the following to your `application.yml` (resources folder):
+
+```yaml reference
+https://github.com/zitadel/zitadel-java/blob/main/api/src/main/resources/application.yml
+```
+
+Note that the `introspection-uri`, `client-id` and `client-secret` are only placeholders. You can either change them in here using the values provided by ZITADEL
+or pass them later on as arguments when starting the application.
+
+### Create example API
+
+Create a `api` package with a `ExampleController.java` file with the content below. This will create an API with three endpoints / methods:
+- `/api/healthz`: can be called by anyone and always returns `OK`
+- `/api/tasks (GET)`: requires authorization and returns the available tasks
+- `/api/tasks (POST)`: requires authorization with granted `admin` role and adds the task to the list
+
+If authorization is required, the token must not be expired and the API has to be part of the audience (either client_id or project_id).
+
+For tests we will use a Personal Access Token or the [Java Spring web example](../login/java-spring).
+
+```java reference
+https://github.com/zitadel/zitadel-java/blob/main/api/src/main/java/demo/app/api/ExampleController.java
+```
+
+## Test API
+
+In case you've created your own application and depending on your development setup you might need to build the application first:
+
+```bash
+mvn clean package -DskipTests
+```
+
+You will need to provide the `introspection-uri` (your ZITADEL domain> /oauth/v2/introspect), the `client-id` and `client-secret` previously created:
+
+```bash
+java \
+ -Dspring.security.oauth2.resourceserver.opaquetoken.introspection-uri= \
+ -Dspring.security.oauth2.resourceserver.opaquetoken.client-id= \
+ -Dspring.security.oauth2.resourceserver.opaquetoken.client-secret= \
+ -jar api/target/api-0.0.2-SNAPSHOT.jar
+```
+
+This could look like:
+
+```bash
+java \
+ -Dspring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://my-domain.zitadel.cloud/oauth/v2/introspect \
+ -Dspring.security.oauth2.resourceserver.opaquetoken.client-id=243861220627644836@example \
+ -Dspring.security.oauth2.resourceserver.opaquetoken.client-secret=WJKLF3kfPOi3optkg9vi3jmfjv8oj32nfiäohj!FSC09RWUSR \
+ -jar web/target/web-0.0.2-SNAPSHOT.jar
+```
+
+### Public endpoint
+
+Now you can call the API by browser or curl. Try the healthz endpoint first:
+
+```bash
+curl -i http://localhost:18090/api/healthz
+```
+
+it should return something like:
+
+```
+HTTP/1.1 200
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 0
+Cache-Control: no-cache, no-store, max-age=0, must-revalidate
+Pragma: no-cache
+Expires: 0
+X-Frame-Options: DENY
+Content-Type: text/plain;charset=UTF-8
+Content-Length: 2
+Date: Mon, 15 Jan 2024 09:07:21 GMT
+
+OK
+```
+
+### Task list
+
+and the task list endpoint:
+
+```bash
+curl -i http://localhost:18090/api/tasks
+```
+
+it will return:
+
+```
+HTTP/1.1 401
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+WWW-Authenticate: Bearer
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 0
+Cache-Control: no-cache, no-store, max-age=0, must-revalidate
+Pragma: no-cache
+Expires: 0
+X-Frame-Options: DENY
+Content-Length: 0
+Date: Mon, 15 Jan 2024 09:07:55 GMT
+```
+
+Get a valid access_token for the API. You can either achieve this by getting an access token with the project_id in the audience
+(e.g. by using the [Spring Boot web example](../login/java-spring)) use a PAT of a service account.
+
+If you provide a valid Bearer Token:
+
+```bash
+curl -i -H "Authorization: Bearer ${token}" http://localhost:18090/api/tasks
+```
+
+it will return an empty list:
+```
+HTTP/1.1 200
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 0
+Cache-Control: no-cache, no-store, max-age=0, must-revalidate
+Pragma: no-cache
+Expires: 0
+X-Frame-Options: DENY
+Content-Type: application/json
+Transfer-Encoding: chunked
+Date: Mon, 15 Jan 2024 09:15:10 GMT
+
+[]
+```
+
+### Try to add a new task
+
+Let's see what happens if you call the tasks endpoint:
+
+```bash
+ curl -i -X POST -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" --data 'my new task' http://localhost:18090/api/tasks
+```
+
+it will complain with a permission denied (missing `admin` role):
+```
+HTTP/1.1 403
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+WWW-Authenticate: Bearer error="insufficient_scope", error_description="The request requires higher privileges than provided by the access token.", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 0
+Cache-Control: no-cache, no-store, max-age=0, must-revalidate
+Pragma: no-cache
+Expires: 0
+X-Frame-Options: DENY
+Content-Length: 0
+Date: Mon, 15 Jan 2024 09:24:39 GMT
+```
+
+### Add admin role
+
+So let's create the role and grant it to the user. To do so, go to your project in ZITADEL Console
+and create the role by selecting `Roles` in the navigation and then clicking on the `New Role` button.
+Finally, create the role as shown below:
+
+
+
+After you have created the role, let's grant it the user, who requested the tasks.
+Click on `Authorization` in the navigation and create a new one by selecting the user and the `admin` role.
+After successful creation, it should look like:
+
+
+
+So you should now be able to add a new task:
+
+```bash
+curl -i -X POST -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" --data 'my new task' http://localhost:18090/api/tasks
+```
+
+which will report back the successful addition:
+```
+HTTP/1.1 200
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 0
+Cache-Control: no-cache, no-store, max-age=0, must-revalidate
+Pragma: no-cache
+Expires: 0
+X-Frame-Options: DENY
+Content-Type: application/json
+Content-Length: 10
+Date: Mon, 15 Jan 2024 09:26:11 GMT
+
+task added
+```
+
+Let's now retrieve the task list again:
+
+```bash
+curl -i -H "Authorization: Bearer ${token}" http://localhost:18090/api/tasks
+```
+
+As you can see your new task is listed:
+```
+HTTP/1.1 200
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 0
+Cache-Control: no-cache, no-store, max-age=0, must-revalidate
+Pragma: no-cache
+Expires: 0
+X-Frame-Options: DENY
+Content-Type: application/json
+Transfer-Encoding: chunked
+Date: Mon, 15 Jan 2024 09:26:48 GMT
+
+["my new task"]
+```
diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index aedb22e6ca..da5a601136 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -178,7 +178,7 @@ module.exports = {
selector: "div#",
},
prism: {
- additionalLanguages: ["csharp", "dart", "groovy", "regex"],
+ additionalLanguages: ["csharp", "dart", "groovy", "regex", "java", "php"],
},
colorMode: {
defaultMode: "dark",
diff --git a/docs/sidebars.js b/docs/sidebars.js
index fbca39eedc..276082eac4 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -16,7 +16,8 @@ module.exports = {
"examples/login/flutter",
"examples/login/nextjs",
"examples/login/go",
- "examples/login/symfony"
+ "examples/login/symfony",
+ "examples/login/java-spring",
],
collapsed: true,
},
@@ -28,6 +29,7 @@ module.exports = {
"examples/secure-api/python-flask",
"examples/secure-api/dot-net",
"examples/secure-api/nodejs-nestjs",
+ "examples/secure-api/java-spring",
],
collapsed: true,
},
diff --git a/docs/static/img/java-spring/api-create-auth.png b/docs/static/img/java-spring/api-create-auth.png
new file mode 100644
index 0000000000..b5d8ee3814
Binary files /dev/null and b/docs/static/img/java-spring/api-create-auth.png differ
diff --git a/docs/static/img/java-spring/api-create-clientid-secret.png b/docs/static/img/java-spring/api-create-clientid-secret.png
new file mode 100644
index 0000000000..f0882b5a83
Binary files /dev/null and b/docs/static/img/java-spring/api-create-clientid-secret.png differ
diff --git a/docs/static/img/java-spring/api-create.png b/docs/static/img/java-spring/api-create.png
new file mode 100644
index 0000000000..1c21cf0706
Binary files /dev/null and b/docs/static/img/java-spring/api-create.png differ
diff --git a/docs/static/img/java-spring/api-project-auth.png b/docs/static/img/java-spring/api-project-auth.png
new file mode 100644
index 0000000000..741c08a558
Binary files /dev/null and b/docs/static/img/java-spring/api-project-auth.png differ
diff --git a/docs/static/img/java-spring/api-project-role.png b/docs/static/img/java-spring/api-project-role.png
new file mode 100644
index 0000000000..a819f6b8d3
Binary files /dev/null and b/docs/static/img/java-spring/api-project-role.png differ
diff --git a/docs/static/img/java-spring/app-create-auth.png b/docs/static/img/java-spring/app-create-auth.png
new file mode 100644
index 0000000000..e54b41bd49
Binary files /dev/null and b/docs/static/img/java-spring/app-create-auth.png differ
diff --git a/docs/static/img/java-spring/app-create-clientid.png b/docs/static/img/java-spring/app-create-clientid.png
new file mode 100644
index 0000000000..76473e47d6
Binary files /dev/null and b/docs/static/img/java-spring/app-create-clientid.png differ
diff --git a/docs/static/img/java-spring/app-create-redirect.png b/docs/static/img/java-spring/app-create-redirect.png
new file mode 100644
index 0000000000..cd1d7657fe
Binary files /dev/null and b/docs/static/img/java-spring/app-create-redirect.png differ
diff --git a/docs/static/img/java-spring/app-create.png b/docs/static/img/java-spring/app-create.png
new file mode 100644
index 0000000000..ca78b1ec0c
Binary files /dev/null and b/docs/static/img/java-spring/app-create.png differ
diff --git a/docs/static/img/java-spring/app-profile.png b/docs/static/img/java-spring/app-profile.png
new file mode 100644
index 0000000000..e20178168d
Binary files /dev/null and b/docs/static/img/java-spring/app-profile.png differ