Expand my Community achievements bar.

Radically easy to access on brand approved content for distribution and omnichannel performant delivery. AEM Assets Content Hub and Dynamic Media with OpenAPI capabilities is now GA.

Building an AEM Custom Authentication Handler for Okta OpenID Connect

Avatar

Employee

11/9/23

LarsAuffarth_0-1699520851348.png
  by Lars Auffarth

Overview

Unlock the secrets of customizing secure authentication in AEM as you're guided through building a custom authentication handler for Okta OpenID Connect. From understanding the OpenID Connect authentication flow to implementing the handler with detailed code snippets, this blog provides a comprehensive roadmap. Learn how to create a demo account on Okta, extract required information, and deploy your custom Authentication Handler for testing. Don't miss the GitHub link for the complete code and try it out yourself!


Q&A

Please use this thread to ask questions relating to this article

7 Comments

Avatar

Level 2

11/23/23

This is a good explanation. However, one use case is missing.
After we have Logged in from the IDP and AEM has received the auth Code and generated access, id and refresh tokens from the Token endpoint, we fetch the user info i.e. userId from the Token Endpoint. The example only explains how we can create a new user and login using new user credentials but does not show how we can login to AEM using the userId received from Okta.
I believe the userId in Okta and AEM should be same, we can lookup the user but to send an AuthInfo object from extractCredentials we need the password login to Sling Repo.
 
SimpleCredentials sc = new SimpleCredentials(userId,
userId.toCharArray());
We don't have the password as the login happened in Okta and AEM only received the authCode how can we login to the SlingRepository using only the userId. Request you to clarify. No example on the internet explains this use case. I am hoping this post will explain it.
Thanks.

 

Avatar

Employee

11/28/23

Hi Oliver,

Thank you for bringing up this point. In AEM, we consider the user to be authenticated after successful authentication with Okta, as outlined in the official authentication code flow. This flow is executed for each unauthenticated request against a resource protected by the authentication handler, positioning AEM as a consumer of this information. In the simplified scenario presented in the blog post, we take specific actions based on this information:

  1. If no user corresponding to the Okta-User-ID is found, we create a user. For simplicity in the example, we assign the exact Okta-User-ID as both the username and password for the AEM user.

  2. If a user exists for the Okta-User-ID, we facilitate a login on behalf of the user using the Okta-User-ID as both the username and password. Subsequently, the user is logged in utilising AEMs token authentication mechanism.

In case you'd like to dive deeper into handling external logins into AEM, I recommend reviewing the External Login Module at https://jackrabbit.apache.org/oak/docs/security/authentication/externalloginmodule.html.

Hope this helps!

Avatar

Level 2

11/28/23

Hello,

 

Your demo documentation is very helpful and detailed. However if you can provide some concrete code sample around authenticating a user via Code in AEM using just the user-id it will be helpful. I managed to do it using a deprecated way. I had to enable Whitelist Administrative Resource Resolver Login.

 

String userId= "aem.user";
repository.loginService("systemUser", null );
Session adminSession = resourceResolver.adaptTo(Session.class);
AuthenticationInfo authinfo = TokenUtil.createCredentials(request, response, this.repository, userId, true);

 

// this Token Utils requires AdminLogin I know there is a new API around this but no concrete examples and the documentation is very poor.

If you can help me with this it will be very great as nobody on the whole internet talks about this.

thanks.

Avatar

Employee

11/28/23

You are correct, TokenUtils is deprecated and shouldn't be used anymore.

Please have a look at the code linked in the blog post, this should answer your question as it does not rely on the deprecated TokenUtil implementation:

https://github.com/larsauffarth/oidc-authentication-handler/blob/main/core/src/main/java/com/oidc/co...

Let me know if this helps.

Avatar

Level 2

11/28/23

This code does not work for an existing user for me. Because it sets (line 128, 129)
SimpleCredentials sc = new SimpleCredentials(userId,
userId.toCharArray());

 

And it says invalid credentials, as this is a user that already exists and it tries to validate the credentials. But I do not want to validate the user as he has already authenticate at OKTA. So this user should be logged in dirrectly without any password sharing. As I do not want to expose any passwords. Hope you get the context.

Avatar

Level 1

12/1/23

Hi, 
It is a good explanation and I implemented this code, It works perfectly fine, One thing I want to ask, is that we do have multiple dispatchers and publishers and on top of that a load balancer is configured. so what needs to be done to make it work in this Architecture pattern, May be sticky session? your suggestion is required on this.

Thanks.

Avatar

Employee

12/2/23

Hi @OliverSapient,
If the user returns using Okta OIDC (in a new session), the authentication in AEM also needs to take place, meaning that we need to sign in either using the username and password or by utilizing the External Login Module.
If you receive an error calling out that the creds are invalid, it seems that the user already existed before and can't be signed in by username:username (you can test this manually as well). I would propose to delete the user and recreate it again using the provided logic.

Hi @AhmedHa,
Indeed, in such a case, we need to make sure that sticky sessions are enabled. Also, you might want to think about configuring user-sync, in case any shared state exists for the users. Also, if you're planning to save the tokens on the user node, I'd exclude those from the sync as these are typically short-lived.