Welcome to Tech Mastery, your expert source for insights into technology and digital strategy. Explore topics like Adobe Experience Manager, AWS, Azure, generative AI, and advanced marketing strategies. Delve into MACH architecture, Jamstack, modern software practices, DevOps, and SEO. Our blog is ideal for tech professionals and enthusiasts eager to stay ahead in digital innovations, from Content Management to Digital Asset Management and beyond.
How to expose Restful Services through JAX-RS(Jersey) in AEM
JAX-RS is a specification that provides portable API’s for developing, exposing and accessing web applications designed and implemented in compliance with principals of REST architectural style.
-RS has annotations to bind specific URI patterns and HTTP methods to individual methods of your Java code. AEM is driven based on REST principals but there is no direct support for creating Restful services.
There are multiple implementations for JAX-RS specifications, this tutorial explains exposing REST based services in AEM through Jersey.
Install JAX-RS Jersey modules
As a first step, install the JAX-RS Jersey connector to AEM server
Some of the important annotation used to define the component
@POST/@GET — HTTP Method supported by the method, the other HTTP methods can be used based on the requirement @Path — URL path, static or dynamic regex based URL’s can be defined @Produces — The output produced by the method e.g MediaType.APPLICATION_JSON and MediaType.TEXT_PLAIN etc @Consumes — The input consumed by the method e.g MediaType.APPLICATION_JSON and MediaType.APPLICATION_FORM_URLENCODED etc
POJO class to map JSON input/Output
package jaxrsservice.core.restservices; public class Product { private String id; private String name; private String title; private String category; private String desc; private String result; public void setId(String id) { this.id=id; } public void setName(String name) { this.name=name; } public void setTitle(String title) { this.title=title; } public void setCategory(String category) { this.category=category; } public void setDesc(String desc) { this.desc=desc; } public void setResult(String result) { this.result=result; } public String getId() { return id; } public String getName() { return name; } public String getTitle() { return title; } public String getCategory() { return category; } public String getDesc() { return desc; } public String getResult() { return result; }
This tutorial explains how to use Adobe Granite OAuth 2.0 Server functionalities to grant resource access to external clients in AEM(Adobe Experience Manager).
The OAuth 2.0 protocol allows the users to grant a third-party web site or application access to the user's protected resources without necessarily revealing their long term credentials or even their identity.
OAuth 2.0 allows clients to access user’s (resource owner’s) resources on resource servers via authorization servers in a secure, reliable, and efficient manner.
Adobe granite OAuth 2.0 server implementation(com.adobe.granite.oauth.server) provides the support for OAuth 2.0 server functionalities in AEM.
Access tokens are the thing that applications use to make API requests on behalf of a user. The access token represents the authorization of a specific application to access specific parts of a user’s data. The external application should receive the access token to access the protected user resources from AEM.
Grants
The OAuth 2.0 specification describes a number of grants (“methods”) for a client application to acquire an access token (which represents a user’s permission for the client to access their data) which can be used to authenticate a request to a protected resource.
The Granite OAuth Server supports the below grant types
Authorization Code
Refresh Token
JWT Bearer Token
Authorization Code:
The authorization code is a temporary code that the client will exchange for an access token. The code itself is obtained from the authorization server where the user gets a chance to see what the information the client is requesting, and approve or deny the request.
When the user authorizes the application, they are redirected back to the application with a temporary code in the URL. The application exchanges that code for the access token. When the application makes the request for the access token. The application retrieves the required resources through the access token, the access token is short-lived e.g 1 hr.
Refresh Token
The Refresh Token grant type is used by clients to exchange a refresh token for an access token when the access token has expired.
This allows clients to continue to have a valid access token without further interaction with the user.
The user grants the approval once to get the refresh token, subsequently, the application request the access token whenever required and access the required resources without the user's online interaction.
JWT Bearer Token
JWT Bearer Token is mainly used for server to server integration, this will helps us to enable the server to server integration without the resource owner interaction e.g retrieve or upload files without user interactions.
The client forms the JWT Token with specific configuration data, the token should be signed by the AEM OAuth Client private key, and exchanges the token to the server to get the access token, the access token can be used to retrieve the protected resources.
Scopes
The scope is a mechanism in OAuth 2.0 to limit an application’s access to a user’s account. An application can request one or more scopes, this information is then presented to the user in the consent screen, and the access token issued to the application will be limited to the scopes granted.
The default scopes supported in AEM
Profile — Get the basic profile details on user authorization.
Offline Access — Access the resources offline without resource owner interaction, the offline Access scope should be used with other scopes to receive the offline refresh token.
Replicate — Replicate the resources.
Custom Scopes
AEM supports to define custom scopes to provide additional access to the external applications, the extensible OAuth scopes allow access control the resources from a client application that is authorized by an end-user.
To define the custom scopes
Define a class implements com.adobe.granite.oauth.server.Scope or com.adobe.granite.oauth.server.ScopeWithPrivileges, extended Scope interface for scopes that defined required privileges on their content paths. The privileges defined in the scope will be created by the OAuth Server using the oauthservice user. It is the responsibility of the scope developer to ensure that oauthservice has the necessary privileges to do this.
Implement the getName and provide the name for the scope e.g “dam_read”
Implement the getDescription and provide the name for the scope e.g “Read DAM Assets”
Implement the getResourcePath and provide the parent resource path that is enabled for the custom scope e.g /content/dam/digital, the scope enables the access to the child resources under /content/dam/digital based on the privileges defined in the scope.
Implement the getPrivileges and provide the required privileges to execute this scope e.g jcr:read, rep:write, etc
Enter a name and redirect URI — After a user successfully authorizes an application, the OAuth server will redirect the user back to the application with an authorization code to the configured redirect URL.
Copy the client id and client secret and keep them safe.
Setup Demo User
Setup up a demo user with all possible profile data and required permissions — complete read-only access and full access for “/content/dam/digital”
Profile Data
Let us now see how to retrieve the profile data of the user.
If required, pass an additional parameter with name state with any random value e.g state=AlbinTest, the same state value will be returned in the response. This will helps us to verify the integrity of the data.
Login with the demo user created in the earlier step, this will display the authorization screen with permissions granted to the external application.
Now the user authorize the request, the user will be returned to the client application with authorization code as the parameter.
Let us now enable a custom scope to read the DAM data, configure as required and deploy the scope
import javax.servlet.http.HttpServletRequest;import org.apache.jackrabbit.api.security.user.User; import org.osgi.service.component.annotations.Component; import com.adobe.granite.oauth.server.Scope; import com.adobe.granite.oauth.server.ScopeWithPrivileges;@Component(service=Scope.class) public class DAMReadScope implements ScopeWithPrivileges{
private static final String DAM_RESOURCE_URI="/content/dam/digital"; private static final String DAM_RESOURCE_READ_SCOPE_NAME="dam_read";
public DAMReadScope() {
}@Override public String getDescription(HttpServletRequest arg0) { return "Read DAM Assets"; }@Override public String getEndpoint() { return null; }@Override public String getName() { return DAM_RESOURCE_READ_SCOPE_NAME; }@Override public String getResourcePath(User user) { return DAM_RESOURCE_URI; }@Override public String[] getPrivileges() { return new String[] {"jcr:read"}; }}
Enable the required access to “oauthservice” service user, I am enabling the read access for root folder “/”, and full access for /content/dam/digital folder(ReadACL is mandatory for the folder configured in the Scope).
As a first step, access the authorization URL and receive the authorization code(refer the profile data section for more details)
Now upload the asset data to /content/dam/digital folder through the access token
curl -H “Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJnNmIzajl2aTk4bDM5ZmFvNzFpdmc0aDlraC1xM2dydGdoaCIsImlzcyI6IkFkb2JlIEdyYW5pdGUiLCJzdWIiOiJhbGJpbm9hdXRoIiwiZXhwIjoxNTk3MjY0NjA4LCJpYXQiOjE1OTcyNjEwMDgsInNjb3BlIjoiZGFtX3dyaXRlIiwiY3R5IjoiYXQifQ.G2C7KLuy7nQ8PlCHfEotxga_p10U_w_acazAe9y0MJo” -X POST -F file=@C:\Users\albin\Desktop\salesforce-cms-connect-to-aem http://localhost:4502/content/dam/digital.createasset.html
The asset is successfully uploaded now.
Offline Access
Let us now retrieve the offline token to access the resources without resource owner interaction every time. I am going to receive the offline access token to fetch the user’s profile data whenever required. The offline_access scope should be combined with other scopes e.g profile to fetch the offline token that can be used whenever required to fetch authorized scope data offline e.g profile
As a first step, access the authorization URL and receive the authorization code to fetch the basic user profile data offline(refer the profile data section for more details)
Receive the Access Token/refresh token(offline access token)with the authorization code
The access token is valid for 3600 sec and can be used to receive the profile data within that time limit, the refresh_token can be used to receive the access_token whenever required to fetch the user’s profile data without the interaction from user.
The access token can be used to fetch the user’s profile data(or other scope data authorized by users)
JWT Bearer Token— Server to Server integration
Let us now see how to use the JWT Bearer token to enable the server to server integration without the resource owner’s intervention.
As a first step, download the AEM OAuth client private key to sign the JWT bearer token also note the private key password(keep the private key and password safe)
Now generate the public certificate from the downloaded private key, execute the below command — Enter the private key password.
Now you should be able to download the assets from /content/dam/digital through the access token(based on the scope used while generating the JWT token)
The access token granted by the OAuth authorization server(AEM) can be used by the clients to access the protected resources from AEM. Sometimes we may need to revoke the access tokens granted to the clients due to various reasons, AEM provides different options to revoke the tokens.
Client Token Revocation — Revoke all the access tokens granted under specific OAuth client. In this scenario, the tokens can be revoked by the user who created/manages the OAuth client in AEM(my case “admin” user).
Now the individual token revocation is enabled, the individual tokens can be revoked through — http://localhost:4502/libs/granite/oauth/content/tokens.html. The individual tokens should be revoked by the user who granted the access tokens, in my case “albin”
Revoking Individual Tokens through Service — AEM provides API to revoke the access tokens granted to the clients.
As a conclusion, Adobe granite OAuth 2.0 server implementation(com.adobe.granite.oauth.server) provides the support for OAuth 2.0 server functionalities in AEM. The AEM OAuth 2.0 server functionalities can be used to manage the protected resources from AEM through external clients. The required grants and scopes can be used to manage the resources, if required custom scopes can be defined to provide custom resource access.