WF 26 + Elytron + OIDC + remote EJB but no WAR in EAR

921 views
Skip to first unread message

Lumír Návrat

unread,
May 18, 2022, 5:30:47 AM5/18/22
to WildFly
Hi,
last days I'm searching how to solve requirement to use new OIDC subsystem for authentication and future authorization in our system. At beginning on Keycloak but in future other provider could be used.

Main problem is that related to the examples by Farah Juma and others expects that exists war module and web application but our application has this architecture:

1. Thick client in Eclipse RCP that calls remote EJB and JMS on WF server
  - user open login dialog in eclipse, input username and password that is later set to AuthenticationContext this way:

    closeContextsSafely();
    AuthenticationConfiguration authenticationConfiguration = AuthenticationConfiguration.empty()
        .useName(login)
        .usePassword(password);
    AuthenticationContext authenticationContext = AuthenticationContext.empty()
        .with(MatchRule.ALL.matchHost(host), authenticationConfiguration);
    AuthenticationContext.getContextManager().setGlobalDefault(authenticationContext);
    currentEjbClientContext = ejbClientContextProvider.createContext(host);
    currentRemoteNamingContext = remoteNamingContextProvider.createContext(host);


=> ejb client isn't authenticated with one static user/password in xml file. but during application run could be changed.

2. WF 26.x server with configured Elytron security. Deployment is ear that contains only one ejb-module.
Elytron has defined own security domain and realm for our deployment

Is this architecture supported?  I got idea that could create on client "web view" dialog that shows the keycloak webpage but how then pass tokens to server and have server authenticated to.

Or exists some other solution that I idon't see?


Current elytron config:
        <subsystem xmlns="urn:wildfly:elytron:15.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
            <providers>
                <aggregate-providers name="combined-providers">
                    <providers name="elytron"/>
                    <providers name="openssl"/>
                </aggregate-providers>
                <provider-loader name="elytron" module="org.wildfly.security.elytron"/>
                <provider-loader name="openssl" module="org.wildfly.openssl"/>
            </providers>
            <audit-logging>
                <file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
            </audit-logging>
            <security-domains>
                <security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
                    <realm name="ManagementRealm" role-decoder="groups-to-roles"/>
                    <realm name="local" role-mapper="super-user-mapper"/>
                </security-domain>
                <security-domain name="hlen" default-realm="hlen-realm" permission-mapper="default-permission-mapper">
                    <realm name="hlen-realm"/>
                </security-domain>
                <security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
                    <realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
                    <realm name="local"/>
                </security-domain>
            </security-domains>
            <security-realms>
                <custom-realm name="wincorejdbc-realm" module="de.ids.acos.et.commons.wincore-login-module" class-name="de.ids.acos.et.commons.auth.win.core.wildfly.JdbcWinCoreSecurityRealm">
                    <configuration>
                        <property name="sql" value="SELECT role FROM um_user WHERE username = ? AND authentication_source='WINDOWS'"/>
                        <property name="data-source" value="acos-et-ds"/>
                        <property name="roles-index" value="1"/>
                        <property name="winauthenticator-factory" value="de.ids.acos.et.commons.auth.win.core.wildfly.WinAuthenticatorFactory"/>
                    </configuration>
                </custom-realm>
                <identity-realm name="local" identity="$local"/>
                <jdbc-realm name="jdbc-internal-realm">
                    <principal-query sql="SELECT role, password FROM um_user WHERE username = ? AND authentication_source='INTERNAL'" data-source="acos-et-ds">
                        <attribute-mapping>
                            <attribute to="Roles" index="1"/>
                        </attribute-mapping>
                        <simple-digest-mapper algorithm="simple-digest-sha-1" password-index="2"/>
                    </principal-query>
                </jdbc-realm>
                <properties-realm name="ApplicationRealm">
                    <users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
                    <groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
                </properties-realm>
                <properties-realm name="ManagementRealm">
                    <users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
                    <groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
                </properties-realm>
                <distributed-realm name="hlen-realm" realms="wincorejdbc-realm jdbc-internal-realm"/>
            </security-realms>
            <mappers>
                <simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
                    <permission-mapping>
                        <principal name="anonymous"/>
                        <permission-set name="default-permissions"/>
                    </permission-mapping>
                    <permission-mapping match-all="true">
                        <permission-set name="login-permission"/>
                        <permission-set name="default-permissions"/>
                    </permission-mapping>
                </simple-permission-mapper>
                <constant-realm-mapper name="local" realm-name="local"/>
                <simple-role-decoder name="groups-to-roles" attribute="groups"/>
                <constant-role-mapper name="super-user-mapper">
                    <role name="SuperUser"/>
                </constant-role-mapper>
            </mappers>
            <permission-sets>
                <permission-set name="login-permission">
                    <permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
                </permission-set>
                <permission-set name="default-permissions">
                    <permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
                    <permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
                    <permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
                </permission-set>
            </permission-sets>
            <http>
                <http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
                    <mechanism-configuration>
                        <mechanism mechanism-name="DIGEST">
                            <mechanism-realm realm-name="ManagementRealm"/>
                        </mechanism>
                    </mechanism-configuration>
                </http-authentication-factory>
                <http-authentication-factory name="application-http-authentication" security-domain="ApplicationDomain" http-server-mechanism-factory="global">
                    <mechanism-configuration>
                        <mechanism mechanism-name="BASIC">
                            <mechanism-realm realm-name="Application Realm"/>
                        </mechanism>
                        <mechanism mechanism-name="FORM"/>
                    </mechanism-configuration>
                </http-authentication-factory>
                <provider-http-server-mechanism-factory name="global"/>
            </http>
            <sasl>
                <sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
                    <mechanism-configuration>
                        <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
                        <mechanism mechanism-name="DIGEST-MD5">
                            <mechanism-realm realm-name="ManagementRealm"/>
                        </mechanism>
                    </mechanism-configuration>
                </sasl-authentication-factory>
                <sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
                    <mechanism-configuration>
                        <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
                        <mechanism mechanism-name="DIGEST-MD5">
                            <mechanism-realm realm-name="ApplicationRealm"/>
                        </mechanism>
                    </mechanism-configuration>
                </sasl-authentication-factory>
                <sasl-authentication-factory name="hlen-sasl" sasl-server-factory="elytron" security-domain="hlen">
                    <mechanism-configuration>
                        <mechanism mechanism-name="PLAIN"/>
                        <mechanism mechanism-name="DIGEST-MD5"/>
                        <mechanism mechanism-name="SIMPLE-DIGEST-SHA-1"/>
                    </mechanism-configuration>
                </sasl-authentication-factory>
                <configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
                    <properties>
                        <property name="wildfly.sasl.local-user.default-user" value="$local"/>
                        <property name="wildfly.sasl.local-user.challenge-path" value="${jboss.server.temp.dir}/auth"/>
                    </properties>
                </configurable-sasl-server-factory>
                <mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
                    <filters>
                        <filter provider-name="WildFlyElytron"/>
                    </filters>
                </mechanism-provider-filtering-sasl-server-factory>
                <provider-sasl-server-factory name="global"/>
            </sasl>
            <tls>
                <key-stores>
                    <key-store name="LocalhostKeyStore">
                        <credential-reference clear-text="deleted"/>
                        <implementation type="JKS"/>
                        <file path="hlen_server_keystore.keystore" relative-to="jboss.server.config.dir"/>
                    </key-store>
                </key-stores>
                <key-managers>
                    <key-manager name="LocalhostKeyManager" key-store="LocalhostKeyStore" alias-filter="hlen_server">
                        <credential-reference clear-text="deleted"/>
                    </key-manager>
                </key-managers>
                <server-ssl-contexts>
                    <server-ssl-context name="LocalhostSslContext" cipher-suite-names="TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" protocols="TLSv1.3" key-manager="LocalhostKeyManager"/>
                </server-ssl-contexts>
            </tls>
            <credential-stores>
                <credential-store name="hlen_store" relative-to="hlen.permanent-configuration.dir" path="hlenstore.jceks">
                    <credential-reference clear-text="deleted"/>
                </credential-store>
            </credential-stores>
        </subsystem>

Farah Juma

unread,
May 24, 2022, 1:49:40 PM5/24/22
to Lumír Návrat, WildFly
For this use case, it sounds like Elytron's BEARER_TOKEN authentication mechanism should be used.

More details can be found in the documentation.

If you're also using Elytron on the client side (either programmatically via an AuthenticationContext or using a wildfly-config.xml file), it's possible to automatically obtain the token to pass to the server. This can be done using the AuthenticationConfiguration#useCredentials method (there's an example here) or by configuring an <oauth2-bearer-token> element in the wildfly-config.xml file (there are some examples here).

--
You received this message because you are subscribed to the Google Groups "WildFly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wildfly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wildfly/d7add51e-b1e1-4dd9-9871-b7ed298ff37bn%40googlegroups.com.

Lumír Návrat

unread,
May 31, 2022, 8:31:32 AM5/31/22
to WildFly
Hi,

thx. with the provided links I was able modify code to call my keycloak.

for others this code on client worked for me:

    AuthenticationConfiguration authenticationConfiguration = AuthenticationConfiguration.empty()
        .useProviders(() -> new Provider[] { new WildFlyElytronSaslOAuth2Provider() })
        .useCredentials(OAuth2CredentialSource.builder(new URL("http://localhost:8888/auth/realms/VIVAVIS/protocol/openid-connect/token"))
            .useResourceOwnerPassword(login, password)
            .clientCredentials("hlen", "dont_tell_me")
            .build());

    AuthenticationContext authenticationContext = AuthenticationContext.empty()
        .with(MatchRule.ALL.matchHost(host),
            authenticationConfiguration.setSaslMechanismSelector(SaslMechanismSelector.NONE.addMechanism("OAUTHBEARER")))
        .with(MatchRule.ALL.matchHost("localhost").matchPort(8888).matchPath("/auth/realms/my_realm/protocol/openid-connect/token"),
            AuthenticationConfiguration.empty()
                .useName(login)
                .usePassword(password));
    AuthenticationContext.getContextManager().setGlobalDefault(authenticationContext);

and this I have in server configuration

                <token-realm name="OAuth2Realm" principal-claim="preferred_username">
                  <oauth2-introspection client-id="hlen"
                          client-secret="dont_tell_me"
                          introspection-url="http://localhost:8888/auth/realms/my_realm/protocol/openid-connect/token/introspect"
                          client-ssl-context="LocalhostSslContext"
                          host-name-verification-policy="ANY" />
                </token-realm>
                <distributed-realm name="hlen-realm" realms="OAuth2Realm wincorejdbc-realm jdbc-internal-realm"/>
            </security-realms>

just what confuse me is that OAuth2CredentialSource is deprecated but without some more info why.

Another question is how could mask client_secret. in server xml file we use MASK-XXXX format but what I understood from docu, clientMaskedCredentials are different and not possible generate it via elytron_tool.bat this tool generate old picket box compatible masked password.

I'm right or not?

And now I'm finding that roles are not propagated from introspection
introspection endpoint return in json my defined roles in this property:

"resource_access": { "hlen": { "roles": [ "guest" ] }, "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } },

but RolesAllowedInterpector returns for category ejb value NONE.

I think that missing is some role mapper or decoder but can't find any existing.

Dne úterý 24. května 2022 v 19:49:40 UTC+2 uživatel Farah Juma napsal:

Farah Juma

unread,
Jun 1, 2022, 3:37:14 PM6/1/22
to WildFly
This has been deprecated because this class will likely be moved to a different package in a future major Elytron release.
 

Another question is how could mask client_secret. in server xml file we use MASK-XXXX format but what I understood from docu, clientMaskedCredentials are different and not possible generate it via elytron_tool.bat this tool generate old picket box compatible masked password.

Since the client-secret is an attribute that supports expressions, it's possible to use an encrypted expression there. See this blog post for more details on that.
 


I'm right or not?

And now I'm finding that roles are not propagated from introspection
introspection endpoint return in json my defined roles in this property:

"resource_access": { "hlen": { "roles": [ "guest" ] }, "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } },

but RolesAllowedInterpector returns for category ejb value NONE.

I think that missing is some role mapper or decoder but can't find any existing.

You'll want to configure and make use of a role-decoder. As an example, if the roles were found in the "scp" claim as follows:

{"scp": ["guest", "user"],
  "anotherClaim": "anotherValue"
   ...
}


Then a simple-role-decoder could be used that maps the "scp" attribute to roles. For cases where the roles are nested within another claim, a custom-role-decoder could be used instead. More information about role decoders can be found in the documentation.

Lumír Návrat

unread,
Jul 11, 2022, 9:59:38 AM7/11/22
to WildFly
Hi Farah again,

after some weeks I return to this topic as I want try Keycloak based on Quarkus and docker-compose with pgsql.

But it didn't works for me now. Reason is probably that I delete the line with useProviders on client so fall to no auth mechanims. When I copy it back I have problem with missing class in Eclipse:

Eclipse didn't see WildFlyElytronSaslOAuth2Provider class. resp. It didn't see in wildfly-client-all.jar file only in own small artifact. When I looked in my local maven repo it is present in jar for 26.0.1 but not for newer 26.1.1 and 27.0.0.Alpha1 it isn't. It is bug or wildfly-elytron-sals-oauth2 artifact is not part of client now?
We are using the wildfly-client-all.jar as lib that is bundled in eclipse plugin and then exported to other plugins in our eclipse based application.

Dne středa 1. června 2022 v 21:37:14 UTC+2 uživatel Farah Juma napsal:

Lumír Návrat

unread,
Jul 12, 2022, 6:21:10 AM7/12/22
to WildFly
So I fixed the libs dependencies. And found now another problem:(

Inside application we are using JMS via activemq-messaging subsystem. Factories etc is created correctly but we register on client to topic and JMS is configured to use security by elytron

code on client:

    TopicConnectionFactory topicConnectionFactory = getTopicConnectionFactory();
    topicConnection = topicConnectionFactory.createTopicConnection("someUserName", "<DONT TELL ME>");

server config file:
        <subsystem xmlns="urn:jboss:domain:messaging-activemq:13.1">
            <server name="default">
                <security elytron-domain="hlen"/>
                <security-setting name="#">
                    <role name="roleName" send="true" consume="true" create-non-durable-queue="true" delete-non-durable-queue="true"/>
                </security-setting>

But result is: AMQ222216: Security problem while authenticating: AMQ229031: Unable to validate user from /192.168.163.83:60609. Username: messaging; SSL certificate subject DN: unavailable

Exists some way how get token and pass token during authentication via JMS? I guess no as from code it seems that expected is only username/password.

During debug I found that
1. SecurityStoreImpl call securityManager.validateUser(username,password)
2. inside ElytronSecurityManager is then called authenticate method and authentication is in fact delegated to Elytron.

=>
I tried create distributed realm that use previous token-realm and 2nd is custom realm that I tried to use OidcSecurityRealm from wildfly-elytron-http-oidc (as I think that this is possible to call on server side the keycloak. REST API?
BUT Principal is NamePrincipal (created here: org.wildfly.security.auth.server.ServerAuthenticationContext.setAuthenticationName(String, boolean))

=> is some way how convert NamePrincipal to OidcPrincipal or there only way is implement completely own custom realm that accept the NamePrincipal and call the token rest endpoint?
Dne pondělí 11. července 2022 v 15:59:38 UTC+2 uživatel Lumír Návrat napsal:
Reply all
Reply to author
Forward
0 new messages