Estou tentando usar o Azure Entra ID (macróbio Azure Active Directory) uma vez que um provedor de identidade SAML2 para um projeto Laravel 11 usando um frontend Inertia, implementando o /24Slides/laravel-saml2 pacote.
Seguindo a documentação para laravel-saml2 e referenciando o Exemplo de Power Pages Para o Azure Entra ID, consigo redirecionar para a página do IdP e fazer login, mas, depois de redirecionado de volta para meu projeto, recebo uma Serialization of 'DOMDocument' is not allowed, unless serialization methods are implemented in a subclass
erro que emite de IlluminateSessionStore::save()
que é chamado de IlluminateSessionMiddlewareStartSession::handleStatefulRequest()
.
ERROR: Serialization of 'DOMDocument' is not allowed, unless serialization methods are implemented in a subclass {"exception":"[object] (Exception(code: 0): Serialization of 'DOMDocument' is not allowed, unless serialization methods are implemented in a subclass at [[project_directory]]vendorlaravelframeworksrcIlluminateSessionStore.php:179)
[stacktrace]
#0 [internal function]: DOMNode->__sleep()
#1 [[project_directory]]vendorlaravelframeworksrcIlluminateSessionStore.php(179): serialize()
#2 [[project_directory]]vendorlaravelframeworksrcIlluminateSessionMiddlewareStartSession.php(245): IlluminateSessionStore->save()
#3 [[project_directory]]vendorlaravelframeworksrcIlluminateSessionMiddlewareStartSession.php(130): IlluminateSessionMiddlewareStartSession->saveSession()
#4 [[project_directory]]vendorlaravelframeworksrcIlluminateSessionMiddlewareStartSession.php(64): IlluminateSessionMiddlewareStartSession->handleStatefulRequest()
#5 [[project_directory]]vendorlaravelframeworksrcIlluminatePipelinePipeline.php(183): IlluminateSessionMiddlewareStartSession->handle()
#6 [[project_directory]]vendorlaravelframeworksrcIlluminateCookieMiddlewareAddQueuedCookiesToResponse.php(37): IlluminatePipelinePipeline->IlluminatePipeline{closure}()
#7 [[project_directory]]vendorlaravelframeworksrcIlluminatePipelinePipeline.php(183): IlluminateCookieMiddlewareAddQueuedCookiesToResponse->handle()
#8 [[project_directory]]vendorlaravelframeworksrcIlluminateCookieMiddlewareEncryptCookies.php(75): IlluminatePipelinePipeline->IlluminatePipeline{closure}()
#9 [[project_directory]]vendorlaravelframeworksrcIlluminatePipelinePipeline.php(183): IlluminateCookieMiddlewareEncryptCookies->handle()
#10 [[project_directory]]vendor24slideslaravel-saml2srcHttpMiddlewareResolveTenant.php(69): IlluminatePipelinePipeline->IlluminatePipeline{closure}()
#11 [[project_directory]]vendorlaravelframeworksrcIlluminatePipelinePipeline.php(183): SlidesSaml2HttpMiddlewareResolveTenant->handle()
[ etc ]
Até onde sei, o corpo da solicitação do Sua visita nos ajuda a continuar oferecendo o melhor para você! do Azure não deve resultar em um objeto DOMDocument, nem deve ter problemas com o Laravel iniciando uma sessão SAML2.
Meu bootstrapapp.php
registro:
withRouting(
web: __DIR__ . '/../routes/web.php',
api: __DIR__ . '/../routes/api.php',
Sua visita nos ajuda a continuar oferecendo o melhor para você! commands: __DIR__ . '/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
AppHttpMiddlewareHandleInertiaRequests::class,
IlluminateHttpMiddlewareAddLinkHeadersForPreloadedAssets::class,
]);
$middleware->statefulApi();
$middleware->alias([
'abilities' => CheckAbilities::class,
'ability' => CheckForAnyAbility::class,
]);
$middleware->appendToGroup('saml', [
IlluminateCookieMiddlewareEncryptCookies::class,
IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class,
IlluminateSessionMiddlewareStartSession::class,
]);
//
})
->withExceptions(function (Exceptions $exceptions) {
$exceptions->respond(function (Response $response, Throwable $exception, Request $request) {
if (!app()->environment(['local', 'testing']) && in_array($response->getStatusCode(), [500, 503, 404, 403])) {
return Inertia::render('ErrorPage', ['status' => $response->getStatusCode()])
->toResponse($request)
->setStatusCode($response->getStatusCode());
} elseif ($response->getStatusCode() === 419) {
return back()->with([
'message' => 'The page expired, please try again.',
]);
}
return $response;
});
})->create();
Meu configsaml2.php
registro:
SlidesSaml2ModelsTenant::class,
/*
|--------------------------------------------------------------------------
| Use built-in routes
|--------------------------------------------------------------------------
|
| If "useRoutes" set to true, the package defines five new routes:
|
| Method | URI | Name
| -------|---------------------------------|------------------
| POST | {routesPrefix}/{uuid}/acs | saml.acs
| GET | {routesPrefix}/{uuid}/login | saml.login
| GET | {routesPrefix}/{uuid}/logout | saml.logout
| GET | {routesPrefix}/{uuid}/metadata | saml.metadata
| GET | {routesPrefix}/{uuid}/sls | saml.sls
|
*/
'useRoutes' => true,
'routesPrefix' => '/saml2',
'routesMiddleware' => ['saml'],
'retrieveParametersFromServer' => false,
'loginRoute' => env('SAML2_LOGIN_URL'),
'logoutRoute' => env('SAML2_LOGOUT_URL'),
'errorRoute' => env('SAML2_ERROR_URL'),
'strict' => true,
'debug' => env('SAML2_DEBUG', env('APP_DEBUG', false)),
'proxyVars' => false,
/*
|--------------------------------------------------------------------------
| Service Provider configuration.
|--------------------------------------------------------------------------
|
| General setting of the service provider.
|
*/
'sp' => [
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
'x509cert' => env('SAML2_SP_CERT_x509', ''),
'privateKey' => env('SAML2_SP_CERT_PRIVATEKEY', ''),
'entityId' => env('SAML2_SP_ENTITYID', ''),
'assertionConsumerService' => [
'url' => '',
],
Sua visita nos ajuda a continuar oferecendo o melhor para você! 'singleLogoutService' => [
'url' => ''
],
],
/*
|--------------------------------------------------------------------------
| OneLogin security settings.
|--------------------------------------------------------------------------
|
|
|
*/
'security' => [
'nameIdEncrypted' => false,
'authnRequestsSigned' => false,
'logoutRequestSigned' => false,
'logoutResponseSigned' => false,
'signMetadata' => false,
'wantMessagesSigned' => false,
'wantAssertionsSigned' => false,
'wantNameIdEncrypted' => false,
'requestedAuthnContext' => true,
],
'contactPerson' => [
'technical' => [
'givenName' => env('SAML2_CONTACT_TECHNICAL_NAME', 'name'),
'emailAddress' => env('SAML2_CONTACT_TECHNICAL_EMAIL', '[email protected]')
],
'support' => [
'givenName' => env('SAML2_CONTACT_SUPPORT_NAME', 'Support'),
'emailAddress' => env('SAML2_CONTACT_SUPPORT_EMAIL', '[email protected]')
],
],
'organization' => [
'en-US' => [
'name' => env('SAML2_ORGANIZATION_NAME', 'Name'),
'displayname' => env('SAML2_ORGANIZATION_NAME', 'Display Name'),
'url' => env('SAML2_ORGANIZATION_URL', 'http://url')
],
],
'load_migrations' => true,
];
Olhar para o erro em si mostra um referência direta à mensagem que não se aplica à minha situação, a problema no github que originalmente levantou problemas com a serialização de objetos DOM em PHP, e várias pessoas com problemas semelhantes em relação Closure
e.
Primeiro tentei modificar o saml
middleware definido em meu bootstrapapp.php
mas remover ou substituir qualquer um dos componentes resultou em um loop de redirecionamento entre meu aplicativo e a página do IdP.
Em seguida, tentei somar uma subclasse com métodos para serializar o objeto DOMDocument, mas sem efeito:
//SUBCLASS
xmlData = $this->saveXML();
return ['xmlData'];
}
public function __wakeup(): void
{
$this->loadXML($this->xmlData);
}
public static function fromUnserializable($DOMDoc){
$serializable = new SerializableDOMDocument();
foreach(get_object_vars($DOMDoc) as $key => $val){
$serializable->$key = $val;
}
return $serializable;
}
}
//MODIFICATIONS TO IlluminateSessionStore::save() TO USE NEW SUBCLASS
public function save()
{
$this->ageFlashData();
$this->prepareErrorBagForSerialization();
$serializable = $this->attributes;
if($this->attributes instanceof DOMDocument){
$serializable = SerializableDOMDocument::fromUnserializable($this->attributes);
}
$this->handler->write($this->getId(), $this->prepareForStorage(
$this->serialization === 'json' ? json_encode($this->attributes) : serialize($serializable)
));
$this->started = false;
}
Neste ponto, tenho quase certeza de que perdi alguma coisa na feição do pacote SAML2, mas não tenho certeza do que pode ser.