Skip to content

Commit

Permalink
Merge pull request quarkusio#38619 from sberyozkin/oidc_devui_include…
Browse files Browse the repository at this point in the history
…_extra_parameters

Pass extra authentication params in the OIDC DevUI code flow redirect URL
  • Loading branch information
gsmet authored Feb 13, 2024
2 parents 85b2093 + e14c3aa commit 490992e
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 5 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 10 additions & 4 deletions docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,9 @@ For more information on how to configure Auth0 and Quarkus to have authorization

So far we have only tested the Quarkus endpoint using OIDC authorization code flow. In this flow you use the browser to access the Quarkus endpoint, Quarkus itself manages the authorization code flow, a user is redirected to Auth0, logs in, is redirected back to Quarkus, Quarkus completes the flow by exchanging the code for the ID, access, and refresh tokens, and works with the ID token representing the successful user authentication. The access token is not relevant at the moment. As mentioned earlier, in the authorization code flow, Quarkus will only use the access token to access downstream services on behalf of the currently authenticated user.

Lets imagine though that the Quarkus endpoint we have developed has to accept `Bearer` access tokens too: it may be that the other Quarkus endpoint which is propagating it to this endpoint or it can be SPA which uses the access token to access the Quarkus endpoint. And Quarkus OIDC DevUI SPA which we already used to analyze the ID token fits perfectly for using the access token available to SPA to test the Quarkus endpoint.
Let's imagine though that the Quarkus endpoint we have developed has to accept `Bearer` access tokens too: it may be that the other Quarkus endpoint which is propagating it to this endpoint or it can be SPA which uses the access token to access the Quarkus endpoint. And Quarkus OIDC DevUI SPA which we already used to analyze the ID token fits perfectly for using the access token available to SPA to test the Quarkus endpoint.

Lets go again to http://localhost:8080/q/dev, select the `OpenId Connect` card, login to Auth0, and check the Access token content:
Let's go again to http://localhost:8080/q/dev-ui, select the `OpenId Connect` card, login to Auth0, and check the Access token content:

image::auth0-devui-accesstoken.png[Auth0 DevUI Access Token]

Expand Down Expand Up @@ -632,7 +632,7 @@ For more information about token propagation, see xref:security-openid-connect-c

We have already looked in detail at how Quarkus OIDC can handle <<opaque-access-tokens>>, but we don't want to propagate Auth0 opaque tokens to micro services which do something useful on behalf on the currently authenticated user, beyond checking its UserInfo.

A microservice which the front-end Quarkus application will access by propagating authorization code flow access tokens to it is represented in the Auth0 dashboard as an `API`. Lets add it in the `Applications/APIs`:
A microservice which the front-end Quarkus application will access by propagating authorization code flow access tokens to it is represented in the Auth0 dashboard as an `API`. Let's add it in the `Applications/APIs`:

image::auth0-api.png[Auth0 API]

Expand Down Expand Up @@ -804,6 +804,12 @@ public class GreetingResource {

Open a browser, access http://localhost:8080/hello and get your name displayed in the browser.

Let's go to http://localhost:8080/q/dev-ui, select the `OpenId Connect` card, login to Auth0, and check the Access token content:

image::auth0-devui-jwt-accesstoken.png[Auth0 DevUI JWT Access Token]

As you can see, the access token is no longer encrypted as shown in the <<opaque-access-tokens>> section and indeed it is in the JWT format now.

[[permission-based-access-control]]
=== Permission Based Access Control

Expand Down Expand Up @@ -939,7 +945,7 @@ quarkus.oidc.client-id=sKQu1dXjHB6r0sra0Y1YCqBZKWXqCkly
quarkus.oidc.credentials.secret=${client-secret}
----

In production, you will distinguish between prod and test level configuration with `%prod.` and `%test.` qualifiers. Lets assume that the above configuration will indeed be prefixed with `%test.` in your real application, with this configuration also including the `%prod.` qualified Auth0 production tenant configuration.
In production, you will distinguish between prod and test level configuration with `%prod.` and `%test.` qualifiers. Let's assume that the above configuration will indeed be prefixed with `%test.` in your real application, with this configuration also including the `%prod.` qualified Auth0 production tenant configuration.

Using `OidcTestClient` to test such configuration requires acquiring a token from the Auth0 dev tenant, using either OAuth2 `password` or `client_credentials` grant, we will try a `password` grant. Make sure the application registered in the Auth0 dashboard allows the `password` grant:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class OidcPropertiesState extends LitState {
logoutUrl: null,
postLogoutUriParam: null,
scopes: null,
authExtraParams: null,
httpPort: 0,
accessToken: null,
idToken: null,
Expand Down Expand Up @@ -73,6 +74,7 @@ class OidcPropertiesState extends LitState {
propertiesState.logoutUrl = response.result.logoutUrl;
propertiesState.postLogoutUriParam = response.result.postLogoutUriParam;
propertiesState.scopes = response.result.scopes;
propertiesState.authExtraParams = response.result.authExtraParams;
propertiesState.httpPort = response.result.httpPort;
propertiesState.oidcProviderName = response.result.oidcProviderName;
propertiesState.oidcApplicationType = response.result.oidcApplicationType;
Expand Down Expand Up @@ -857,6 +859,7 @@ export class QwcOidcProvider extends QwcHotReloadElement {
_signInToOidcProviderAndGetTokens() {
const clientId = this._getClientId();
const scopes = propertiesState.scopes ?? '';
const authExtraParams = propertiesState.authExtraParams ?? '';

let address;
let state;
Expand All @@ -880,7 +883,8 @@ export class QwcOidcProvider extends QwcHotReloadElement {
+ "&redirect_uri=" + this._getEncodedPath()
+ "&scope=" + scopes + "&response_type=" + responseType
+ "&response_mode=query&prompt=login&nonce=" + QwcOidcProvider._makeId()
+ "&state=" + state;
+ "&state=" + state
+ authExtraParams;
}

_getEncodedPath() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.oidc.runtime;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
Expand All @@ -13,6 +14,7 @@
import io.quarkus.oidc.common.runtime.OidcCommonUtils;
import io.quarkus.oidc.common.runtime.OidcConstants;
import io.quarkus.oidc.runtime.providers.KnownOidcProviders;
import io.smallrye.config.SmallRyeConfig;

public class OidcConfigPropertySupplier implements Supplier<String> {
private static final String AUTH_SERVER_URL_CONFIG_KEY = "quarkus.oidc.auth-server-url";
Expand All @@ -23,6 +25,7 @@ public class OidcConfigPropertySupplier implements Supplier<String> {
TOKEN_PATH_CONFIG_KEY, AUTH_PATH_CONFIG_KEY);
private static final String OIDC_PROVIDER_CONFIG_KEY = "quarkus.oidc.provider";
private static final String SCOPES_KEY = "quarkus.oidc.authentication.scopes";
private static final String AUTH_EXTRA_PARAMS_KEY = "quarkus.oidc.authentication.extra-params";
private String oidcConfigProperty;
private String defaultValue;
private boolean urlProperty;
Expand Down Expand Up @@ -119,6 +122,22 @@ public String get(Config config) {
} else {
return OidcConstants.OPENID_SCOPE;
}
} else if (AUTH_EXTRA_PARAMS_KEY.equals(oidcConfigProperty)) {
StringBuilder sb = new StringBuilder();
if (config instanceof SmallRyeConfig) {
Optional<Map<String, String>> extraParams = ((SmallRyeConfig) config).getOptionalValues(oidcConfigProperty,
String.class,
String.class);
if (extraParams.isPresent()) {
for (Map.Entry<String, String> entry : extraParams.get().entrySet()) {
if (entry.getKey().equals(OidcConstants.TOKEN_SCOPE)) {
continue;
}
sb.append("&").append(entry.getKey()).append("=").append(OidcCommonUtils.urlEncode(entry.getValue()));
}
}
}
return sb.toString();
} else {
return checkUrlProperty(config.getOptionalValue(oidcConfigProperty, String.class),
providerConfig, config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ public class OidcDevUiRuntimePropertiesDTO {
private static final String END_SESSION_PATH_CONFIG_KEY = CONFIG_PREFIX + "end-session-path";
private static final String POST_LOGOUT_URI_PARAM_CONFIG_KEY = CONFIG_PREFIX + "logout.post-logout-uri-param";
private static final String SCOPES_KEY = CONFIG_PREFIX + "authentication.scopes";
private static final String AUTH_EXTRA_PARAMS_KEY = CONFIG_PREFIX + "authentication.extra-params";
private final String clientId;
private final String clientSecret;
private final String authorizationUrl;
private final String tokenUrl;
private final String logoutUrl;
private final String postLogoutUriParam;
private final String scopes;
private final String authExtraParams;
private final int httpPort;
private final String oidcProviderName;
private final String oidcApplicationType;
Expand Down Expand Up @@ -49,6 +51,7 @@ public class OidcDevUiRuntimePropertiesDTO {
this.logoutUrl = new OidcConfigPropertySupplier(END_SESSION_PATH_CONFIG_KEY, logoutUrl, true).get(config);
this.postLogoutUriParam = new OidcConfigPropertySupplier(POST_LOGOUT_URI_PARAM_CONFIG_KEY).get(config);
this.scopes = new OidcConfigPropertySupplier(SCOPES_KEY).get(config);
this.authExtraParams = new OidcConfigPropertySupplier(AUTH_EXTRA_PARAMS_KEY).get(config);
this.httpPort = httpPort;
this.oidcProviderName = oidcProviderName;
this.oidcApplicationType = oidcApplicationType;
Expand Down Expand Up @@ -92,6 +95,10 @@ public String getScopes() {
return scopes;
}

public String getAuthExtraParams() {
return authExtraParams;
}

public int getHttpPort() {
return httpPort;
}
Expand Down

0 comments on commit 490992e

Please sign in to comment.