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?
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);