use BookStack\Exceptions\UserRegistrationException;
use Exception;
use OneLogin\Saml2\Auth;
+use OneLogin\Saml2\Constants;
use OneLogin\Saml2\Error;
use OneLogin\Saml2\IdPMetadataParser;
use OneLogin\Saml2\ValidationError;
*/
public function __construct(
RegistrationService $registrationService,
- LoginService $loginService,
- GroupSyncService $groupSyncService
- )
- {
+ LoginService $loginService,
+ GroupSyncService $groupSyncService
+ ) {
$this->config = config('saml2');
$this->registrationService = $registrationService;
$this->loginService = $loginService;
return [
'url' => $toolKit->login($returnRoute, [], false, false, true),
- 'id' => $toolKit->getLastRequestID(),
+ 'id' => $toolKit->getLastRequestID(),
];
}
*
* @throws Error
*/
- public function logout(): array
+ public function logout(User $user): array
{
$toolKit = $this->getToolkit();
$returnRoute = url('/');
try {
- $url = $toolKit->logout($returnRoute, [], null, null, true);
+ $url = $toolKit->logout(
+ $returnRoute,
+ [],
+ $user->email,
+ null,
+ true,
+ Constants::NAMEID_EMAIL_ADDRESS
+ );
$id = $toolKit->getLastRequestID();
} catch (Error $error) {
if ($error->getCode() !== Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED) {
* @throws JsonDebugException
* @throws UserRegistrationException
*/
- public function processAcsResponse(?string $requestId): ?User
+ public function processAcsResponse(string $requestId, string $samlResponse): ?User
{
+ // The SAML2 toolkit expects the response to be within the $_POST superglobal
+ // so we need to manually put it back there at this point.
+ $_POST['SAMLResponse'] = $samlResponse;
$toolkit = $this->getToolkit();
$toolkit->processResponse($requestId);
$errors = $toolkit->getErrors();
public function processSlsResponse(?string $requestId): ?string
{
$toolkit = $this->getToolkit();
- $redirect = $toolkit->processSLO(true, $requestId, false, null, true);
+ // The $retrieveParametersFromServer in the call below will mean the library will take the query
+ // parameters, used for the response signing, from the raw $_SERVER['QUERY_STRING']
+ // value so that the exact encoding format is matched when checking the signature.
+ // This is primarily due to ADFS encoding query params with lowercase percent encoding while
+ // PHP (And most other sensible providers) standardise on uppercase.
+ $redirect = $toolkit->processSLO(true, $requestId, true, null, true);
$errors = $toolkit->getErrors();
if (!empty($errors)) {
protected function loadOneloginServiceProviderDetails(): array
{
$spDetails = [
- 'entityId' => url('/saml2/metadata'),
+ 'entityId' => url('/saml2/metadata'),
'assertionConsumerService' => [
'url' => url('/saml2/acs'),
],
return [
'baseurl' => url('/saml2'),
- 'sp' => $spDetails,
+ 'sp' => $spDetails,
];
}
/**
* Extract the details of a user from a SAML response.
+ *
* @return array{external_id: string, name: string, email: string, saml_id: string}
*/
protected function getUserDetails(string $samlID, $samlAttributes): array
return [
'external_id' => $externalId,
- 'name' => $this->getUserDisplayName($samlAttributes, $externalId),
- 'email' => $email,
- 'saml_id' => $samlID,
+ 'name' => $this->getUserDisplayName($samlAttributes, $externalId),
+ 'email' => $email,
+ 'saml_id' => $samlID,
];
}
if ($this->config['dump_user_details']) {
throw new JsonDebugException([
- 'id_from_idp' => $samlID,
- 'attrs_from_idp' => $samlAttributes,
+ 'id_from_idp' => $samlID,
+ 'attrs_from_idp' => $samlAttributes,
'attrs_after_parsing' => $userDetails,
]);
}
}
$user = $this->registrationService->findOrRegister(
- $userDetails['name'], $userDetails['email'], $userDetails['external_id']
+ $userDetails['name'],
+ $userDetails['email'],
+ $userDetails['external_id']
);
if ($user === null) {