Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.
SOLVED

How to get metadata of Dam Asset while accessing an asset through Asset API from 3rd party app

Avatar

Level 2

I am using the JWT Bearer Token Server to Server Integration.

and trying to access the asset in AEM through the asset api endpoint from 3rd party application:

I am facing few issues:

1. Everytime a new access token is generated using the JWT Bearer and a call is made to access asset, a new oauth user is generated. Is there a way to restrict this and only use one oauth user over multiple tokens.

2. The oauth user generated has only read permissions on content/dam assets, because of which im not getting the asset metadata in the response as it requires either write/modify/all privileges. Is there a way where we can add these generated oauth user to some group.

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Level 2

Resolved!

The "oauthservice" user defines the privileges on the newly created oauth users when a request with access token comes in.

Added the required privileges to oauthservice system user on the desired path to help it assign the required ACL's to newly generated oauth users.

View solution in original post

12 Replies

Avatar

Correct answer by
Level 2

Resolved!

The "oauthservice" user defines the privileges on the newly created oauth users when a request with access token comes in.

Added the required privileges to oauthservice system user on the desired path to help it assign the required ACL's to newly generated oauth users.

Avatar

Administrator
@kpsolanki_1204, thank you for sharing the solution with community. This would help in posterity. looking forward for more contribution from SME like you.


Kautuk Sahni

Avatar

Level 1
What were the required privileges? I tried adding jcr:all to /content/dam, but this wasn't sufficient. My http assets api requests return empty metadata property. When logged into the instance, or using a local development token instead of JWT, metadata is populated. We are trialing AEM and using a consultant who suggested that perhaps our license doesn't support metadata in this context.

Avatar

Level 1

@Stuart-Downing

So every time an access token is generated from AEM, a new user is generated which is a shadow of the "oauthservice" user and has similar permission to it. 

Points to check:

1. Are you providing the required permissions to "oauthservice" user. If yes, Check the new oauth user which gets generated and check if similar permissions are inherited by it.

2. Are you providing the required privileges at the correct path.

3. For accessing the metadata of node, only jcr:read privilege over the content node wont work. I had given additional jcr:write permission to the user for getting the metadata. You will need to add this scope in "getPrivileges" method of ScopeWithPrivileges implementation class as well.

Avatar

Level 1
1. I have no insight into the user generation process you describe. Presumably this user "lives" as long as the lifespan of the access token returned by the JWT exchange process. After the exchange process, I searched the user/group lists for plausible candidates ("oauth", "api", "cloud", "integration") with no luck.

Avatar

Employee Advisor
@kpsolanki_1204 - It would be greate if you could share the code to get the metadata with jwt token and required priviledges on it. Additionally, where do you see the new oauth user getting generated with each call?

Avatar

Level 1

1. I have no insight into the user generation process you describe. Presumably this user "lives" as long as the lifespan of the access token returned by the JWT exchange process. After the exchange process, I searched the user/group lists for plausible candidates ("oauth", "api", "cloud", "integration") with no luck.

2. I am presuming that adding jcr:all to a path will cause that permission to be inherited by all folders/assets below that path. If I'm mistaken about this then applying to /content/dam might be insufficient

 

Avatar

Level 2

@Stuart-Downing

The user should get created under /home/users/oauth everytime a access token is generated. By default the access token is valid for an hour and post expiry the respective oauthuser gets purged.

 

"jcr:all" permissions on the dam path should take care of the permissions issue, but not sure why its not working for you.

 

Also please check the Adobe Granite OAuth Server Authentication Handler configuration in felix console is properly configured. 

Double check if this configuration is active via System Console →Main →JASS Console

kpsolanki_1204_0-1623849166028.png

 

 

Avatar

Level 1
I see what appears to be the oauthservice user here: /home/users/system/cq:services/internal/oauth/{{UUID}} It has property rep:principalName=oauthservice. I don't know what pattern to look for to find the temporary user created. I don't have permission to access /system/console.

Avatar

Level 1

The code (PHP) follows. As to the question about seeing a new oauth user, I don't see any new users created after the JWT exchange.  Also, the JWT exchange is successful and generate an access token that results in a successful asset api request that is identical to my request made with a local development token, except that the metadata property is empty.

 

<?php
declare(strict_types=1);

namespace LAZ\objects\shared\AdobeExperienceManager\services;


use Doctrine\Common\Cache\Cache;
use Exception;
use Firebase\JWT\JWT;
use GuzzleHttp\Client;
use LAZ\objects\library\HttpStatus;
use LAZ\objects\library\Router\Exception\ForbiddenException;
use LAZ\objects\shared\AdobeExperienceManager\AccessTokenProvider;
use LAZ\objects\shared\AdobeExperienceManager\LazAemInstance;
use stdClass;

class AemServiceCredentialAuthService implements AccessTokenProvider
{
const EXPIRATION_SECONDS_INTERVAL = 60 * 60;
private Cache $cache;
private LazAemInstance $aemInstance;

public function __construct(Cache $cache, LazAemInstance $aemInstance)
{
$this->cache = $cache;
$this->aemInstance = $aemInstance;
}

public function getServiceCredentials(): ?stdClass {
$aemEnv = $_ENV['AEM_AUTHORING_ENV'];
$aemVaultDir = $_ENV['AEM_VAULT_DIR'];
$serviceTokenFile = "$aemVaultDir/service_token_$aemEnv.json";
$credentialsStr = file_get_contents($serviceTokenFile);
if ($credentialsStr === false) {
throw new Exception("Unable to read AEM service credentials from $serviceTokenFile");
}
return json_decode($credentialsStr);
}

public function exchangeJwt(stdClass $options stdClass {
$imsEndpoint = $options->imsEndpoint;
$clientId = $options->clientId;
$jwtPayload = [
'iss' => $options->issuer,
'sub' => $options->subject,
'exp' => $options->expirationTimeSeconds,
'aud' => "https://$imsEndpoint/c/$clientId",
];
foreach ($options->metascopes as $metascope) {
$metascopeKey = "https://$imsEndpoint/s/$metascope";
$jwtPayload[$metascopeKey] = true;
}
$jwtToken = JWT::encode($jwtPayload, $options->privateKey, 'RS256');
$client = new Client();
$uri = "https://$imsEndpoint/ims/exchange/jwt";
$response = $client->post($uri, [
'form_params' => [
'client_id' => $options->clientId,
'client_secret' => $options->clientSecret,
'jwt_token' => $jwtToken,
],
// 'debug' => fopen($_ENV['LOG_PATH'] . '/GuzzleHttp.log', 'a+'),
]);
$statusCode = $response->getStatusCode();
if ($response->getStatusCode() !== HttpStatus::OK) {
if (in_array($response->getStatusCode(), [HttpStatus::UNAUTHORIZED, HttpStatus::FORBIDDEN])) {
throw new ForbiddenException();
}
throw new Exception("Bad status: $statusCode @ $uri\n");
}
return json_decode($response->getBody()->getContents());
}

public function getAccessToken(): string {
$cacheKey = 'AemServiceCredential.AccessToken.' . $this->aemInstance->getAuthoringHost();
$accessToken = $this->cache->fetch($cacheKey);
if ($accessToken === FALSE) {
$accessToken = $this->getAccessTokenFromServiceCredentials();
$this->cache->save($cacheKey, $accessToken);
}
return $accessToken->access_token;
}

public function getAccessTokenFromServiceCredentials(): stdClass {
$serviceCredentials = $this->getServiceCredentials();
return $this->exchangeJwt((object)[
'imsEndpoint' => $serviceCredentials->integration->imsEndpoint,
'issuer' => $serviceCredentials->integration->org,
'subject' => $serviceCredentials->integration->id,
'expirationTimeSeconds' => time() + self::EXPIRATION_SECONDS_INTERVAL,
'metascopes' => explode(',', $serviceCredentials->integration->metascopes),
'clientId' => $serviceCredentials->integration->technicalAccount->clientId,
'clientSecret' => $serviceCredentials->integration->technicalAccount->clientSecret,
'privateKey' => $serviceCredentials->integration->privateKey,
]);
}
}

Avatar

Administrator
@Stuart-Downing, Great start in the AEM Community. AEM community need SME's like you. Keep the great work going.


Kautuk Sahni