Skip to content

Commit

Permalink
Use the default tenant resolver if the custom one does not resolve a …
Browse files Browse the repository at this point in the history
…tenant
  • Loading branch information
sberyozkin committed Nov 24, 2023
1 parent 315ec1e commit 21fbe9d
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 85 deletions.
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 @@ -586,48 +586,12 @@ user `alice` exists in both tenants, for the application they are distinct users
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 @@ public class CustomTenantResolver implements TenantResolver {

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 @@ -775,6 +778,8 @@ public class CustomTenantConfigResolver implements TenantConfigResolver {

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;
}
}

0 comments on commit 21fbe9d

Please sign in to comment.