keycloakkeycloak-serviceskeycloak-rest-api

Keycloak User Action Mail URL Issue


Good day all. I am trying to send a user actions mail using the keycloak execute actions mail API.

{{base_path}}/admin/realms/{{realm_name}}/users/{{user_id}}/execute-actions-email?redirect_uri={{redirect_uri}}&client_id={{client_id}}

For initiating the mail, I am calling the user action API from one of my custom API. So base path used is http://localhost:8080. So when sending the user action mail the URL also become http://localhost:8080 instead of may https://temp.testkeycloak.in domain.

The issue when using the URL https://temp.testkeycloak.in directly inside custom API is we cant call the API. It will have error like,

2024-04-29 10:12:17,326 INFO  [com.feathersoft.keycloak.organization.api.OrganizationController] (executor-thread-100) Exception createOrganizationUser: java.net.ConnectException
    at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:573)
    at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:123)
    at com.feathersoft.keycloak.organization.util.RequestManagement.put(RequestManagement.java:72)
    at com.feathersoft.keycloak.organization.api.MemberManagerController.sendUserActionMail(MemberManagerController.java:283)
    at com.feathersoft.keycloak.organization.api.OrganizationController.createOrganizationUser(OrganizationController.java:839)
    at com.feathersoft.keycloak.organization.api.OrganizationController$quarkusrestinvoker$createOrganizationUser_13ddb69a39990bc4e703b1568717ad6a98b5b13b.invoke(Unknown Source)
    at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
    at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
    at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:840)
    Caused by: java.net.ConnectException
    at java.net.http/jdk.internal.net.http.common.Utils.toConnectException(Utils.java:1055)
    at java.net.http/jdk.internal.net.http.PlainHttpConnection.connectAsync(PlainHttpConnection.java:198)
    at java.net.http/jdk.internal.net.http.AsyncSSLConnection.connectAsync(AsyncSSLConnection.java:56)
    at java.net.http/jdk.internal.net.http.Http2Connection.createAsync(Http2Connection.java:378)
    at java.net.http/jdk.internal.net.http.Http2ClientImpl.getConnectionFor(Http2ClientImpl.java:128)
    at java.net.http/jdk.internal.net.http.ExchangeImpl.get(ExchangeImpl.java:93)
    at java.net.http/jdk.internal.net.http.Exchange.establishExchange(Exchange.java:343)
    at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl0(Exchange.java:475)
    at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl(Exchange.java:380)
    at java.net.http/jdk.internal.net.http.Exchange.responseAsync(Exchange.java:372)
    at java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:408)
    at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsyncImpl$7(MultiExchange.java:449)
    at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934)
    at java.base/java.util.concurrent.CompletableFuture.uniHandleStage(CompletableFuture.java:950)
    at java.base/java.util.concurrent.CompletableFuture.handle(CompletableFuture.java:2340)
    at java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:439)
    at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsync0$2(MultiExchange.java:341)
    at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1773)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.execute(HttpClientImpl.java:157)
    at java.base/java.util.concurrent.CompletableFuture.completeAsync(CompletableFuture.java:2673)
    at java.net.http/jdk.internal.net.http.MultiExchange.responseAsync(MultiExchange.java:294)
    at java.net.http/jdk.internal.net.http.HttpClientImpl.sendAsync(HttpClientImpl.java:654)
    at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:552)
    ... 15 more
    Caused by: java.nio.channels.UnresolvedAddressException
    at java.base/sun.nio.ch.Net.checkAddress(Net.java:149)
    at java.base/sun.nio.ch.Net.checkAddress(Net.java:157)
    at java.base/sun.nio.ch.SocketChannelImpl.checkRemote(SocketChannelImpl.java:816)
    at java.base/sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:839)
    at java.net.http/jdk.internal.net.http.PlainHttpConnection.lambda$connectAsync$0(PlainHttpConnection.java:183)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
    at java.net.http/jdk.internal.net.http.PlainHttpConnection.connectAsync(PlainHttpConnection.java:185)
    ... 38 more

Can anyone help me with this? Is there any SPI replacement for this option?


Solution

  • I found the solution for this with keycloak SPI. We can use keycloak EmailTemplateProvider class for sending actions mail.

    Imports done for using SPI.

        import jakarta.ws.rs.core.UriBuilder;
        import org.keycloak.models.KeycloakContext;
        import org.keycloak.models.RealmModel;
        import org.keycloak.models.UserModel;
        import org.keycloak.email.EmailTemplateProvider;
        import org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken;
    

    Code to send actions emails.

        KeycloakContext context = this.session.getContext();
        RealmModel realm = context.getRealm();
        UserModel user = this.session.users().getUserById(realm, userID);
        int validityInSecs = realm.getActionTokenGeneratedByUserLifespan();
        int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;
        ExecuteActionsActionToken token = new ExecuteActionsActionToken(
                userID, user.getEmail(), absoluteExpirationInSecs, actionsList, redirectURI,
                clientId);
    
        UriBuilder builder = LoginActionsService.actionTokenProcessor(session.getContext().getUri());
        builder.queryParam("key", token.serialize(session, realm, session.getContext().getUri()));
    
        String link = builder.build(realm.getName()).toString();
    
        EmailTemplateProvider emailTemplateProvider = session.getProvider(EmailTemplateProvider.class);
        emailTemplateProvider
                .setRealm(realm)
                .setUser(user)
                .sendExecuteActions(link, validityInSecs);