Add support for MS Corporate URIs - fixes #134, fixes #143, fixes #147

This commit is contained in:
Bubka 2023-07-06 17:10:19 +02:00
parent 244a7adbe7
commit 48bdf69d4e
3 changed files with 59 additions and 3 deletions

View File

@ -445,9 +445,28 @@ class TwoFAccount extends Model implements Sortable
try {
$this->generator = Factory::loadFromProvisioningUri($isSteamTotp ? str_replace('otpauth://steam', 'otpauth://totp', $uri) : $uri);
} catch (\Exception|\Throwable $ex) {
throw ValidationException::withMessages([
'uri' => __('validation.custom.uri.regex', ['attribute' => 'uri']),
]);
// Hack for Microsoft corporate 2FAs for whom the Issuer query parameter != the Issuer aside the account
$parsed_uri = \OTPHP\Url::fromString($uri);
$pathChunks = explode(':', rawurldecode(mb_substr($parsed_uri->getPath(), 1)));
$service = $pathChunks[0];
$issuer = data_get($parsed_uri->getQuery(), 'issuer', $service);
if (count($pathChunks) == 2 && strtolower($issuer) == 'microsoft' && strcasecmp($issuer, $service) != 0) {
$newUri = str_replace($pathChunks[0] . ':', '', rawurldecode($uri));
try {
$this->generator = Factory::loadFromProvisioningUri($newUri);
$this->generator->setLabel($service . '_' . $this->generator->getLabel());
} catch (\Exception|\Throwable $ex) {
throw ValidationException::withMessages([
'uri' => __('validation.custom.uri.regex', ['attribute' => 'uri']),
]);
}
} else {
throw ValidationException::withMessages([
'uri' => __('validation.custom.uri.regex', ['attribute' => 'uri']),
]);
}
}
// As loadFromProvisioningUri() accept URI without label (nor account nor service) we check

View File

@ -58,6 +58,12 @@ class OtpTestData
const TOTP_FULL_CUSTOM_URI_NO_IMG = 'otpauth://totp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&period=' . self::PERIOD_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM;
const MICROSOFT = 'Microsoft';
const ORGANIZATION = 'MyOrganization';
const TOTP_MICROSOFT_CORPORATE_URI_MISMATCHING_ISSUER = 'otpauth://totp/' . self::ORGANIZATION . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::MICROSOFT;
const TOTP_FULL_CUSTOM_URI = self::TOTP_FULL_CUSTOM_URI_NO_IMG . '&image=' . self::IMAGE;
const HOTP_FULL_CUSTOM_URI_NO_IMG = 'otpauth://hotp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&counter=' . self::COUNTER_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM;
@ -72,6 +78,8 @@ class OtpTestData
const INVALID_OTPAUTH_URI = 'otpauth://Xotp/' . self::ACCOUNT . '?secret=' . self::SECRET;
const INVALID_OTPAUTH_URI_MISMATCHING_ISSUER = 'otpauth://totp/' . self::MICROSOFT . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE;
const STEAM_TOTP_URI = 'otpauth://totp/' . self::STEAM . ':' . self::ACCOUNT . '?secret=' . self::STEAM_SECRET . '&issuer=' . self::STEAM . '&digits=' . self::DIGITS_STEAM . '&period=30&algorithm=' . self::ALGORITHM_DEFAULT;
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP = [

View File

@ -142,6 +142,25 @@ class TwoFAccountModelTest extends FeatureTestCase
$this->assertEquals(null, $twofaccount->icon);
}
/**
* @test
*/
public function test_fill_with_ms_corporate_totp_uri_returns_correct_value()
{
$twofaccount = new TwoFAccount;
$twofaccount->fillWithURI(OtpTestData::TOTP_MICROSOFT_CORPORATE_URI_MISMATCHING_ISSUER);
$this->assertEquals('totp', $twofaccount->otp_type);
$this->assertEquals(OtpTestData::TOTP_MICROSOFT_CORPORATE_URI_MISMATCHING_ISSUER, $twofaccount->legacy_uri);
$this->assertEquals(OtpTestData::MICROSOFT, $twofaccount->service);
$this->assertEquals(OtpTestData::ORGANIZATION . '_' . OtpTestData::ACCOUNT, $twofaccount->account);
$this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
$this->assertEquals(OtpTestData::DIGITS_DEFAULT, $twofaccount->digits);
$this->assertEquals(OtpTestData::PERIOD_DEFAULT, $twofaccount->period);
$this->assertEquals(null, $twofaccount->counter);
$this->assertEquals(OtpTestData::ALGORITHM_DEFAULT, $twofaccount->algorithm);
}
/**
* @test
*/
@ -228,6 +247,16 @@ class TwoFAccountModelTest extends FeatureTestCase
$twofaccount->fillWithURI(OtpTestData::INVALID_OTPAUTH_URI);
}
/**
* @test
*/
public function test_fill_with_invalid_uri_with_mismatching_issuer_returns_ValidationException()
{
$this->expectException(\Illuminate\Validation\ValidationException::class);
$twofaccount = new TwoFAccount;
$twofaccount->fillWithURI(OtpTestData::INVALID_OTPAUTH_URI_MISMATCHING_ISSUER);
}
/**
* @test
*/