Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the default tenant resolver if the custom one does not resolve a tenant #37252

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 44 additions & 39 deletions docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -583,51 +583,15 @@
[[static-tenant-resolution]]
== Static tenant configuration resolution

When you set multiple tenant configurations in the `application.properties` file, you only need to specify how the tenant identifier gets resolved.

Check warning on line 586 in docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'. Raw Output: {"message": "[Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'.", "location": {"path": "docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc", "range": {"start": {"line": 586, "column": 65}}}, "severity": "INFO"}
To configure the resolution of the tenant identifier, use one of the following options:

* <<default-tenant-resolver>>
* <<tenant-resolver>>
* <<default-tenant-resolver>>
* <<annotations-tenant-resolver>>

[[default-tenant-resolver]]
=== Default resolution

The default resolution for a tenant identifier is convention based, whereby the authentication request must include the tenant identifier in the last segment of the request path.

The following `application.properties` example shows how you can configure two tenants named `google` and `github`:

[source,properties]
----
# Tenant 'google' configuration
quarkus.oidc.google.provider=google
quarkus.oidc.google.client-id=${google-client-id}
quarkus.oidc.google.credentials.secret=${google-client-secret}
quarkus.oidc.google.authentication.redirect-path=/signed-in

# Tenant 'github' configuration
quarkus.oidc.github.provider=google
quarkus.oidc.github.client-id=${github-client-id}
quarkus.oidc.github.credentials.secret=${github-client-secret}
quarkus.oidc.github.authentication.redirect-path=/signed-in
----

In this example, both tenants configure OIDC `web-app` applications to use an authorization code flow to authenticate users and also require session cookies to get generated after the authentication has taken place.
After either Google or GitHub authenticates the current user, the user gets returned to the `/signed-in` area for authenticated users, for example, a secured resource path on the JAX-RS endpoint.

Finally, to complete the default tenant resolution, set the following configuration property:

[source,properties]
----
quarkus.http.auth.permission.login.paths=/google,/github
quarkus.http.auth.permission.login.policy=authenticated
----

If the endpoint is running on `http://localhost:8080`, you can also provide UI options for users to log in to either `http://localhost:8080/google` or `http://localhost:8080/github`, without having to add specific`/google` or `/github` JAX-RS resource paths.
Tenant identifiers are also recorded in the session cookie names after the authentication is completed.
Therefore, authenticated users can access the secured application area without requiring either the `google` or `github` path values to be included in the secured URL.

Default resolution can also work for Bearer token authentication but it might be less practical in this case because a tenant identifier will always need to be set as the last path segment value.
These tenant resolution options will be tried in turn, in the order they are listed, until the tenant id gets resolved.
If the tenant id remains unresolved (`null`) in the end then the default (unnamed) tenant configuration will be selected.

[[tenant-resolver]]
=== Resolve with `TenantResolver`
Expand Down Expand Up @@ -672,6 +636,45 @@

In this example, the value of the last request path segment is a tenant ID, but if required, you can implement a more complex tenant identifier resolution logic.

[[default-tenant-resolver]]
=== Default resolution

The default resolution for a tenant identifier is convention based, whereby the authentication request must include the tenant identifier in the last segment of the request path.

The following `application.properties` example shows how you can configure two tenants named `google` and `github`:

[source,properties]
----
# Tenant 'google' configuration
quarkus.oidc.google.provider=google
quarkus.oidc.google.client-id=${google-client-id}
quarkus.oidc.google.credentials.secret=${google-client-secret}
quarkus.oidc.google.authentication.redirect-path=/signed-in

# Tenant 'github' configuration
quarkus.oidc.github.provider=google
quarkus.oidc.github.client-id=${github-client-id}
quarkus.oidc.github.credentials.secret=${github-client-secret}
quarkus.oidc.github.authentication.redirect-path=/signed-in
----

In this example, both tenants configure OIDC `web-app` applications to use an authorization code flow to authenticate users and also require session cookies to get generated after the authentication has taken place.
After either Google or GitHub authenticates the current user, the user gets returned to the `/signed-in` area for authenticated users, for example, a secured resource path on the JAX-RS endpoint.

Finally, to complete the default tenant resolution, set the following configuration property:

[source,properties]
----
quarkus.http.auth.permission.login.paths=/google,/github
quarkus.http.auth.permission.login.policy=authenticated
----

If the endpoint is running on `http://localhost:8080`, you can also provide UI options for users to log in to either `http://localhost:8080/google` or `http://localhost:8080/github`, without having to add specific`/google` or `/github` JAX-RS resource paths.
Tenant identifiers are also recorded in the session cookie names after the authentication is completed.
Therefore, authenticated users can access the secured application area without requiring either the `google` or `github` path values to be included in the secured URL.

Default resolution can also work for Bearer token authentication but it might be less practical in this case because a tenant identifier will always need to be set as the last path segment value.

Check warning on line 676 in docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'. Raw Output: {"message": "[Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'.", "location": {"path": "docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc", "range": {"start": {"line": 676, "column": 150}}}, "severity": "INFO"}

Check warning on line 676 in docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc", "range": {"start": {"line": 676, "column": 165}}}, "severity": "INFO"}

[[annotations-tenant-resolver]]
=== Resolve with annotations

Expand Down Expand Up @@ -773,8 +776,10 @@
}
----

The `OidcTenantConfig` returned from this method is the same used to parse the `oidc` namespace configuration from the `application.properties`. You can populate it using any of the settings supported by the `quarkus-oidc` extension.

Check warning on line 779 in docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc", "range": {"start": {"line": 779, "column": 138}}}, "severity": "INFO"}

If the dynamic tenant resolver returns `null` then a <<static-tenant-resolution>> will be attempted next.

=== Tenant resolution for OIDC `web-app` applications

The simplest option for resolving OIDC `web-app` application configuration is to follow the steps described in the <<default-tenant-resolver>> section.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,12 @@ private TenantConfigContext getStaticTenantContext(RoutingContext context) {
if (tenantId == null && context.get(CURRENT_STATIC_TENANT_ID_NULL) == null) {
if (tenantResolver.isResolvable()) {
tenantId = tenantResolver.get().resolve(context);
} else if (tenantConfigBean.getStaticTenantsConfig().size() > 0) {
}

if (tenantId == null && tenantConfigBean.getStaticTenantsConfig().size() > 0) {
tenantId = defaultStaticTenantResolver.resolve(context);
}

if (tenantId == null) {
tenantId = context.get(OidcUtils.TENANT_ID_ATTRIBUTE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,10 @@ public String resolve(RoutingContext context) {
if (path.endsWith("code-flow") || path.endsWith("code-flow/logout")) {
return "code-flow";
}
if (path.endsWith("code-flow-encrypted-id-token-jwk")) {
return "code-flow-encrypted-id-token-jwk";
}
if (path.endsWith("code-flow-encrypted-id-token-pem")) {
return "code-flow-encrypted-id-token-pem";
}
if (path.endsWith("code-flow-form-post") || path.endsWith("code-flow-form-post/front-channel-logout")) {
return "code-flow-form-post";
}
if (path.endsWith("code-flow-user-info-only")) {
return "code-flow-user-info-only";
}
if (path.endsWith("code-flow-user-info-github")) {
return "code-flow-user-info-github";
}
if (path.endsWith("bearer-user-info-github-service")) {
return "bearer-user-info-github-service";
}
if (path.endsWith("code-flow-user-info-github-cached-in-idtoken")) {
return "code-flow-user-info-github-cached-in-idtoken";
}
if (path.endsWith("code-flow-token-introspection")) {
return "code-flow-token-introspection";
}
if (path.endsWith("bearer")) {
return "bearer";
}
if (path.endsWith("bearer-id")) {
return "bearer-id";
}
if (path.endsWith("bearer-required-algorithm")) {
return "bearer-required-algorithm";
}
if (path.endsWith("bearer-azure")) {
return "bearer-azure";
}
if (path.endsWith("bearer-no-introspection")) {
return "bearer-no-introspection";
}
if (path.endsWith("bearer-role-claim-path")) {
return "bearer-role-claim-path";
}
if (path.endsWith("bearer-key-without-kid-thumbprint")) {
return "bearer-key-without-kid-thumbprint";
}
if (path.endsWith("bearer-wrong-role-path")) {
return "bearer-wrong-role-path";
}

return null;
}
}
Loading