Skip to content

Commit 36bce2e

Browse files
author
Conduct AI
committed
fix: Can't use a profile that used aws login for temporary credentials (closes #3300)
When a profile with role_arn uses source_profile pointing to a profile configured via `aws login` (which sets login_session), the SDK was failing with 'No credentials present in INI profile' because loadRoleProfile() always used CredentialProvider::ini() to resolve source profile credentials, which only handles static aws_access_key_id/aws_secret_access_key credentials. Fix: In loadRoleProfile(), detect the credential type of the source profile and use the appropriate provider: - If source profile has login_session, use CredentialProvider::login() - If source profile has sso_session or sso_start_url, use CredentialProvider::sso() - Otherwise, fall back to CredentialProvider::ini() (existing behavior) This mirrors how the AWS CLI resolves source_profile credentials.
1 parent 71513f9 commit 36bce2e

2 files changed

Lines changed: 90 additions & 3 deletions

File tree

src/Credentials/CredentialProvider.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,9 +745,26 @@ private static function loadRoleProfile(
745745
$config['preferStaticCredentials'] = true;
746746
$sourceCredentials = null;
747747
if (!empty($roleProfile['source_profile'])){
748-
$sourceCredentials = call_user_func(
749-
CredentialProvider::ini($sourceProfileName, $filename, $config)
750-
)->wait();
748+
$sourceProfile = $profiles[$sourceProfileName];
749+
if (!empty($sourceProfile['login_session'])) {
750+
// Source profile uses aws login (console credentials)
751+
$region = $sourceProfile['region']
752+
?? $config['region']
753+
?? getEnv(self::ENV_REGION)
754+
?: null;
755+
$sourceCredentials = call_user_func(
756+
CredentialProvider::login($sourceProfileName, ['region' => $region])
757+
)->wait();
758+
} elseif (!empty($sourceProfile['sso_session']) || !empty($sourceProfile['sso_start_url'])) {
759+
// Source profile uses SSO credentials
760+
$sourceCredentials = call_user_func(
761+
CredentialProvider::sso($sourceProfileName, $filename, $config)
762+
)->wait();
763+
} else {
764+
$sourceCredentials = call_user_func(
765+
CredentialProvider::ini($sourceProfileName, $filename, $config)
766+
)->wait();
767+
}
751768
} else {
752769
$sourceCredentials = self::getCredentialsFromSource(
753770
$profileName,

tests/Credentials/CredentialProviderTest.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,76 @@ public function testEnsuresSourceProfileHasCredentials(): void
10881088
call_user_func(CredentialProvider::ini())->wait();
10891089
}
10901090

1091+
public function testCreatesFromRoleArnWithLoginSessionSourceProfile(): void
1092+
{
1093+
$awsDir = $this->createAwsHome();
1094+
1095+
// Config file with a profile that has role_arn pointing to a login_session source_profile
1096+
$ini = <<<EOT
1097+
[profile project-monitor]
1098+
role_arn = arn:aws:iam::012345678910:role/project-monitor
1099+
source_profile = aszeremi-local
1100+
region = us-east-1
1101+
[profile aszeremi-local]
1102+
login_session = arn:aws:iam::123456789012:user/aszeremi-local
1103+
region = us-east-1
1104+
EOT;
1105+
file_put_contents($awsDir . '/config', $ini);
1106+
1107+
// Create login cache directory and token file for aszeremi-local
1108+
$cacheDir = $awsDir . '/login/cache';
1109+
mkdir($cacheDir, 0777, true);
1110+
1111+
$sessionHash = hash('sha256', trim('arn:aws:iam::123456789012:user/aszeremi-local'));
1112+
$tokenFile = $cacheDir . '/' . $sessionHash . '.json';
1113+
1114+
$expiration = (new DateTimeResult('+1 hour'))->format('Y-m-d\TH:i:s\Z');
1115+
$tokenData = json_encode([
1116+
'accessToken' => [
1117+
'accessKeyId' => 'loginSourceKey',
1118+
'secretAccessKey' => 'loginSourceSecret',
1119+
'sessionToken' => 'loginSourceToken',
1120+
'accountId' => '123456789012',
1121+
'expiresAt' => $expiration
1122+
],
1123+
'tokenType' => 'aws_sigv4',
1124+
'refreshToken' => 'testRefreshToken',
1125+
'idToken' => 'testIdToken',
1126+
'clientId' => 'arn:aws:signin:::devtools/same-device',
1127+
'dpopKey' => '-----BEGIN EC PRIVATE KEY-----
1128+
MHcCAQEEIFDZHUzOG1Pzq+6F0mjMlOSp1syN9LRPBuHMoCFXTcXhoAoGCCqGSM49
1129+
AwEHoUQDQgAE9qhj+KtcdHj1kVgwxWWWw++tqoh7H7UHs7oXh8jBbgF47rrYGC+t
1130+
djiIaHK3dBvvdE7MGj5HsepzLm3Kj91bqA==
1131+
-----END EC PRIVATE KEY-----'
1132+
]);
1133+
file_put_contents($tokenFile, $tokenData);
1134+
1135+
$result = [
1136+
'Credentials' => [
1137+
'AccessKeyId' => 'assumedKey',
1138+
'SecretAccessKey' => 'assumedSecret',
1139+
'SessionToken' => 'assumedToken',
1140+
'Expiration' => DateTimeResult::fromEpoch(time() + 10)
1141+
],
1142+
];
1143+
1144+
$sts = $this->getTestClient('Sts');
1145+
$this->addMockResults($sts, [new Result($result)]);
1146+
1147+
$config = [
1148+
'stsClient' => $sts,
1149+
'region' => 'us-east-1',
1150+
];
1151+
1152+
$creds = call_user_func(
1153+
CredentialProvider::ini('project-monitor', $awsDir . '/config', $config)
1154+
)->wait();
1155+
1156+
$this->assertSame('assumedKey', $creds->getAccessKeyId());
1157+
$this->assertSame('assumedSecret', $creds->getSecretKey());
1158+
$this->assertSame('assumedToken', $creds->getSecurityToken());
1159+
}
1160+
10911161
public function testLegacySsoProfileProvider(): void
10921162
{
10931163
$awsDir = $this->createAwsHome();

0 commit comments

Comments
 (0)