Using external email service for UPDATE_PROFILE emails

Hi,

Our backend is currently using the KeyCloak Admin Client API (Java) to

  • Create users
  • Create roles
  • Assign roles to users
  • Executing actions emails (“UPDATE_PASSWORD”, “UPDATE_PROFILE”, “VERIFY_EMAIL”)

Our flow however needs to support the following scenario :

  • Instead of using the executeActionsEmail API to have the users complete their profile and allowing them to login we would like to use an external email service to send out user invitation emails.
  • So the update profile email (containing the /login-actions/action-token?key=eyJhbG… link) needs to be sent using an external mail service, and not KeyCloak.

Is there a way to use the KeyCloak API to generate login actions URLs for a user inside a keycloak realm that we can then pass on to the external email service ?

1 Like

I am not an expert, but I couldn’t see a way to do it using the API. If you build a custom extension or required action, you can make a custom action token that way.

Also, there are a few user contributed extensions that allow using other email providers. E.g. GitHub - dasniko/keycloak-aws-ses-email-provider: Drop-in Email Provider SPI replacement for Keycloak to send emails via AWS Simple Email Service (SES). Demo purposes only which might serve as a model for replacing the built-in email system.

Thx for the reply … this seems to change the way the underlying email transport works (SMTP vs AWS SDK) but not how email templates can be externalized.

Might give SO a try and report back should I hear anything. Seems to be a valid use-case that we would be able to externalize email templates and have more control over how signup email are sent, how they should look and what information they can contain (additional application s-ecific context for example that can be communicated during signup that might be beyond what Keycloak knows).

Hello ,

I have the same use case , i tried with Keycloak extension email provider but it does not work .
In fact i want to send a custom mail with confirmation link (containing the /login-actions/action-token?key=eyJhbG… ) .

But i am not able to generate this link as session.getContext().getAuthenticationSession() is always null when i update mail .

Here is my code :

public class CustomFreeMarkerEmailTemplateProvider implements EmailTemplateProvider {

    protected KeycloakSession session;
    /**
     * authenticationSession can be null for some email sendings, it is filled only for email sendings performed as part of the authentication session (email verification, password reset, broker link
     * etc.)!
     */
    protected AuthenticationSessionModel authenticationSession;
    protected FreeMarkerUtil freeMarker;
    protected RealmModel realm;
    protected UserModel user;
    protected final Map<String, Object> attributes = new HashMap<>();
	
	public CustomFreeMarkerEmailTemplateProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
        this.session = session;
        this.freeMarker = freeMarker;
    }
	
	 @Override
    public void sendEvent(Event event) throws EmailException {        
        if(EventType.UPDATE_EMAIL.equals(event.getType())) {
        	
			
	    // !!!!!!!!!!! authSession always null when i update mail from account page !!!!!!!!!!!
		AuthenticationSessionModel authSession = session.getContext().getAuthenticationSession();

		String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authSession).getEncodedId();   
		int absoluteExpirationInSecs = Time.currentTime() + 60;
		VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId, user.getEmail(), authSession.getClient().getClientId());
		UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo),
				authSession.getClient().getClientId(), authSession.getTabId());
		String confirmationLink = builder.build(realm.getName()).toString();
			
			
        }

    }

}

Someone has an idea how to get this confirmation link easily ?

@boussoufiane
Did you solve your problem?

And how did you manage to set your CustomEmailTemplateProvider as default?

I am currently trying to use the SPI and it shows up in the admin console, but is not used.

Setting it as default via CLI startup script failed, the only hint i found online used the deprecated standalone.json and the only mention inside the code is a json used in testing.

You can set it by doing :

            <spi name="emailTemplate">
                <provider name="freemarker" enabled="false"/>
            </spi>

Thank you, i think i am a small step further now :slight_smile:

i currently get a Nullpointer on
this.session.getProvider(EmailTemplateProvider.class) ( running on v11 )
but that is more than before, thanks a lot @boussoufiane

the authSession is null because you want to get it after an UPDATE EMAIL event , and the authSession will be used after a login Event , and that why the authSession is used on a verify_email ( because the verify_email is trigered after a login attent,

and the authSession is used to auto login after click on the link

and here an exemple how you can build the confirmation link

 int validityInSecs = realm.getActionTokenGeneratedByUserLifespan(VerifyEmailActionToken.TOKEN_TYPE);
                int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;

                VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, null, user.getEmail(), "dealweb");

                UriBuilder builder = LoginActionsService.actionTokenProcessor(session.getContext().getUri());
                builder.queryParam("key", token.serialize(session, realm, session.getContext().getUri()));

                String link = builder.build(realm.getName()).toString();

Hope that will help you.

If you’re only interested in obtaining this signup URL and want to do the mail sending yourself, is exposing a REST resources on the realm (RealmResourceProvider) be the way to go to embed the above code ?

Use-case is our backend that would create a keycloak user and then use that REST resource to obtain the signup URL for that user.