277 lines
8.7 KiB
PHP
277 lines
8.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
usesApiSuite();
|
|
|
|
it('logs in with valid customer credentials', function (): void {
|
|
api_test_covers('POST /auth/login', 'happy');
|
|
|
|
api_fixtures()->setModuleConfig('reCAPTCHA', 'enabled', 'false');
|
|
$user = api_fixtures()->createUser([
|
|
'display_name' => 'API Login User',
|
|
'password_plaintext' => 'Secret123!',
|
|
]);
|
|
|
|
$response = api_client()->post('/auth/login', [
|
|
'customer_number' => $user['customer_number'],
|
|
'password' => $user['password_plaintext'],
|
|
]);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
expect($response->data())
|
|
->toBeArray()
|
|
->toHaveKey('token')
|
|
->and($response->data()['token'])
|
|
->toBeString()
|
|
->toHaveLength(64);
|
|
});
|
|
|
|
it('rejects invalid login payloads and credentials', function (): void {
|
|
api_test_covers('POST /auth/login', 'failure');
|
|
|
|
api_fixtures()->setModuleConfig('reCAPTCHA', 'enabled', 'false');
|
|
$user = api_fixtures()->createUser([
|
|
'password_plaintext' => 'Secret123!',
|
|
]);
|
|
|
|
$missingPassword = api_client()->post('/auth/login', [
|
|
'customer_number' => $user['customer_number'],
|
|
]);
|
|
|
|
$missingPassword
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Missing required parameters: password');
|
|
|
|
$invalidCredentials = api_client()->post('/auth/login', [
|
|
'customer_number' => $user['customer_number'],
|
|
'password' => 'wrong-password',
|
|
]);
|
|
|
|
$invalidCredentials
|
|
->assertStatus(401)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Invalid credentials');
|
|
});
|
|
|
|
it('returns the cached auth session payload for a valid token', function (): void {
|
|
api_test_covers('GET /auth/session', 'happy');
|
|
|
|
$session = api_fixtures()->createUserSession(['list_departments'], [
|
|
'display_name' => 'Session User',
|
|
]);
|
|
|
|
api_fixtures()->cacheAuthSessionForUser(
|
|
$session['user'],
|
|
$session['token'],
|
|
['list_departments'],
|
|
[
|
|
'two_factor_enabled' => false,
|
|
]
|
|
);
|
|
api_fixtures()->setModuleConfig('economic', 'transactionDraftCustomerNumber', '445566', 'int');
|
|
api_fixtures()->setModuleConfig('economic', 'defaultDepartmentId', '75', 'int');
|
|
|
|
$response = api_client()->get('/auth/session', $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
expect($response->data())
|
|
->toBeArray()
|
|
->toHaveKey('customer_number', $session['user']['customer_number'])
|
|
->toHaveKey('two_factor_enabled', false)
|
|
->and($response->data()['permissions'])
|
|
->toContain('list_departments')
|
|
->and($response->data()['runtime_config']['economic']['transaction_draft_customer_number'] ?? null)
|
|
->toBe(445566)
|
|
->and($response->data()['runtime_config']['economic']['default_distribution_department_id'] ?? null)
|
|
->toBe(75);
|
|
});
|
|
|
|
it('includes economic runtime config for uncached auth sessions', function (): void {
|
|
api_test_covers('GET /auth/session', 'happy');
|
|
|
|
api_fixtures()->setModuleConfig('economic', 'transactionDraftCustomerNumber', '556677', 'int');
|
|
api_fixtures()->setModuleConfig('economic', 'defaultDepartmentId', '65', 'int');
|
|
$session = api_fixtures()->createUserSession(['list_departments'], [
|
|
'display_name' => 'Fresh Session User',
|
|
]);
|
|
|
|
$response = api_client()->get('/auth/session', $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
expect($response->data())
|
|
->toBeArray()
|
|
->toHaveKey('customer_number', $session['user']['customer_number'])
|
|
->and($response->data()['runtime_config']['economic']['transaction_draft_customer_number'] ?? null)
|
|
->toBe(556677)
|
|
->and($response->data()['runtime_config']['economic']['default_distribution_department_id'] ?? null)
|
|
->toBe(65);
|
|
});
|
|
|
|
it('rejects invalid auth session tokens', function (): void {
|
|
api_test_covers('GET /auth/session', 'auth');
|
|
|
|
$response = api_client()->get('/auth/session', [
|
|
'Authorization' => 'Bearer invalid-token',
|
|
]);
|
|
|
|
$response
|
|
->assertStatus(401)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Invalid token');
|
|
});
|
|
|
|
it('logs out and invalidates the token for future session calls', function (): void {
|
|
api_test_covers('GET /auth/logout', 'happy');
|
|
|
|
$session = api_fixtures()->createUserSession(['list_departments'], [
|
|
'display_name' => 'Logout User',
|
|
]);
|
|
|
|
api_fixtures()->cacheAuthSessionForUser(
|
|
$session['user'],
|
|
$session['token'],
|
|
['list_departments']
|
|
);
|
|
|
|
$logoutResponse = api_client()->get('/auth/logout', $session['headers']);
|
|
|
|
$logoutResponse
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Logged out');
|
|
|
|
$followUpSession = api_client()->get('/auth/session', $session['headers']);
|
|
|
|
$followUpSession
|
|
->assertStatus(401)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Invalid token');
|
|
});
|
|
|
|
it('logs out and invalidates cached subuser sessions', function (): void {
|
|
api_test_covers('GET /auth/logout', 'happy');
|
|
|
|
$user = api_fixtures()->createUser();
|
|
|
|
$session = api_fixtures()->createSubuserSession((int)$user['customer_number'], [], [
|
|
'username' => 'logout-driver',
|
|
]);
|
|
|
|
$warmCache = api_client()->get('/subusers/me', $session['headers']);
|
|
|
|
$warmCache
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$logoutResponse = api_client()->get('/auth/logout', $session['headers']);
|
|
|
|
$logoutResponse
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Logged out');
|
|
|
|
$followUpProfile = api_client()->get('/subusers/me', $session['headers']);
|
|
|
|
$followUpProfile
|
|
->assertStatus(401)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Unauthorized');
|
|
});
|
|
|
|
it('scopes passkey challenges to the requested principal type', function (): void {
|
|
api_test_covers('POST /auth/passkey/challenge', 'auth');
|
|
|
|
api_fixtures()->setModuleConfig('reCAPTCHA', 'enabled', 'false');
|
|
$user = api_fixtures()->createUser([
|
|
'display_name' => 'Passkey Customer',
|
|
]);
|
|
$subuser = api_fixtures()->createSubuser([
|
|
'username' => 'passkey-driver',
|
|
]);
|
|
api_fixtures()->createPasskey([
|
|
'user_id' => (int)$user['id'],
|
|
'is_subuser' => false,
|
|
'credential_id' => 'customer-passkey-credential',
|
|
]);
|
|
api_fixtures()->createPasskey([
|
|
'user_id' => (int)$subuser['id'],
|
|
'is_subuser' => true,
|
|
'credential_id' => 'subuser-passkey-credential',
|
|
]);
|
|
|
|
$subuserChallenge = api_client()->post('/auth/passkey/challenge', [
|
|
'principal_type' => 'subuser',
|
|
'username' => 'passkey-driver',
|
|
]);
|
|
$userChallenge = api_client()->post('/auth/passkey/challenge', [
|
|
'principal_type' => 'user',
|
|
'customer_number' => (int)$user['customer_number'],
|
|
]);
|
|
|
|
$subuserChallenge
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
$userChallenge
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$subuserCredentials = $subuserChallenge->data()['publicKey']['allowCredentials'] ?? [];
|
|
$userCredentials = $userChallenge->data()['publicKey']['allowCredentials'] ?? [];
|
|
|
|
$subuserCredentialIds = array_column($subuserCredentials, 'id');
|
|
$userCredentialIds = array_column($userCredentials, 'id');
|
|
|
|
expect($subuserCredentialIds)->toContain('subuser-passkey-credential');
|
|
expect($subuserCredentialIds)->not->toContain('customer-passkey-credential');
|
|
expect($userCredentialIds)->toContain('customer-passkey-credential');
|
|
expect($userCredentialIds)->not->toContain('subuser-passkey-credential');
|
|
|
|
$tokenRepo = new \objects\tokens_o();
|
|
$subuserChallengeToken = (string)($subuserChallenge->data()['challenge_token'] ?? '');
|
|
$userChallengeToken = (string)($userChallenge->data()['challenge_token'] ?? '');
|
|
if ($subuserChallengeToken !== '') {
|
|
$tokenRepo->delete($subuserChallengeToken);
|
|
}
|
|
if ($userChallengeToken !== '') {
|
|
$tokenRepo->delete($userChallengeToken);
|
|
}
|
|
});
|
|
|
|
it('rejects invalid logout tokens', function (): void {
|
|
api_test_covers('GET /auth/logout', 'auth');
|
|
|
|
$response = api_client()->get('/auth/logout', [
|
|
'Authorization' => 'Bearer invalid-token',
|
|
]);
|
|
|
|
$response
|
|
->assertStatus(401)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Invalid token');
|
|
});
|