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.
Solved! Go to Solution.
Topics help categorize Community content and increase your ability to discover relevant content.
Views
Replies
Total Likes
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.
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.
Views
Replies
Total Likes
Views
Replies
Total Likes
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.
Views
Replies
Total Likes
Views
Replies
Total Likes
Views
Replies
Total Likes
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
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
Views
Replies
Total Likes
Views
Replies
Total Likes
Views
Replies
Total Likes
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 $optionsstdClass {
$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,
]);
}
}
Views
Replies
Total Likes
Views
Likes
Replies