gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
¿Qué significa "cerrar sesión" en algo? ¿Como desconectarse de una API? Bueno, son dos cosas. En primer lugar, significa invalidar el token que tengas, si es posible. Por ejemplo, si tienes un token de API, le dirías a la API:
Haz que este token de API deje de ser válido.
En el caso de la autenticación de sesión, es básicamente lo mismo: significa eliminar la sesión del almacén de sesiones.
La segunda parte de "desconectarse" es hacer que quien esté utilizando el token lo "olvide". Si tuvieras un token de API en JavaScript, lo eliminarías de JavaScript. En el caso de la autenticación de sesión, significa eliminar la cookie.
En cualquier caso, vamos a añadir la posibilidad de cerrar la sesión de nuestra autenticación basada en sesión. Volviendo a SecurityController
, como antes, necesitamos una ruta y un controlador, aunque este controlador nunca será llamado. Llamaré al método logout()
y devolveremos void
. Verás por qué en un segundo. Dale a esto un Route
de/logout
y name: app_logout
:
... lines 1 - 10 | |
class SecurityController extends AbstractController | |
{ | |
... lines 13 - 26 | |
'/logout', name: 'app_logout') ( | |
public function logout(): void | |
{ | |
... line 30 | |
} | |
} |
La razón por la que he elegido void
es porque vamos a lanzar una excepción desde dentro del método. Hemos creado esto completamente porque necesitamos una ruta: el sistema de seguridad de Symfony interceptará las cosas antes de que se llame al controlador:
... lines 1 - 10 | |
class SecurityController extends AbstractController | |
{ | |
... lines 13 - 26 | |
'/logout', name: 'app_logout') ( | |
public function logout(): void | |
{ | |
throw new \Exception('This should never be reached!'); | |
} | |
} |
Para activar esa magia, en security.yaml
, añade una clave llamada logout
con path
debajo configurada con ese nuevo nombre de ruta: app_logout
:
security: | |
... lines 2 - 11 | |
firewalls: | |
... lines 13 - 15 | |
main: | |
... lines 17 - 22 | |
logout: | |
path: app_logout | |
... lines 25 - 50 |
Esto activa un oyente que ahora está atento a las peticiones a /logout
. Cuando haya una petición a /logout
, cerrará la sesión del usuario y lo redirigirá.
Muy bien, aquí, nuestra aplicación Vue cree que no estamos conectados, pero lo estamos: podemos verlo en la barra de herramientas de depuración web. Y si vamos manualmente a /logout
... ¡boom! Ya hemos cerrado la sesión de verdad.
Hace un momento hemos visto que, aunque hayamos iniciado sesión y la actualicemos, nuestra aplicación Vue no tiene ni idea de que hemos iniciado sesión. ¿Cómo podríamos solucionarlo? Una idea sería crear una ruta API /me
. Entonces, al cargarse, nuestra aplicación Vue podría hacer una petición AJAX a esa ruta... que devolvería null
o la información del usuario actual. Pero las rutas /me
no son RESTful. Y hay una forma mejor: volcar la información del usuario en JavaScript al cargar la página.
Hay dos formas diferentes de hacerlo. La primera es estableciendo una variable global. Por ejemplo, en templates/base.html.twig
, en realidad no importa dónde, pero dentro del cuerpo, añade una etiqueta script
. Y aquí digamos window.user =
y luego{{ app.user|serialize }}
. Serializa en jsonld
y añade una |raw
para que no escape la salida: queremos JSON en bruto:
<html> | |
... lines 3 - 15 | |
<body> | |
<script> | |
window.user = {{ app.user|serialize('jsonld')|raw }}; | |
</script> | |
{% block body %}{% endblock %} | |
</body> | |
</html> |
¿No es genial? En un minuto, lo leeremos desde nuestro JavaScript. Si refrescamos ahora mismo y miramos el código fuente, ¡sí! Vemos window.user = null
. Y luego, cuando iniciemos sesión y actualicemos la página, fíjate: ¡ window.user =
y una enorme cantidad de datos!
Pero ocurre algo misterioso: ¡tiene los campos correctos! Fíjate bien, tiene email
, username
y luego dragonTreasures
, que es lo que son todas estas cosas. Además, correctamente, no tiene roles
ni password
.
¡Así que parece que está leyendo correctamente nuestros grupos de normalización! Pero, ¿cómo es eso posible? Sólo estamos diciendo "serializa este usuario a jsonld
". Esto no tiene nada que ver con la API Platform y no está siendo procesado por ella. Pero... nuestros grupos de normalización están configurados en API Platform. Entonces, ¿cómo sabe el serializador que debe utilizarlos?
La respuesta, por lo que sé, es que funciona... en parte por accidente. Durante la serialización, API Platform ve que estamos serializando un "recurso API" y busca los metadatos de esta clase.
Eso está bien... pero en realidad no es perfecto... y de todas formas me gusta ser explícito. Pasa un 2º argumento a serializar, que es el contexto y establece groups
enuser:read
:
<html> | |
... lines 3 - 15 | |
<body> | |
<script> | |
window.user = {{ app.user|serialize('jsonld', { | |
'groups': ['user:read'] | |
})|raw }}; | |
</script> | |
... lines 22 - 23 | |
</body> | |
</html> |
Ahora, observa lo que ocurre cuando actualizamos. Como antes, se expondrán las propiedades correctas enUser
. Pero fíjate en la propiedad dragonTreasures
incrustada. ¡Woh, ha cambiado! Antes estaba mal: incluía todo, no sólo lo que había dentro del grupo user:read
.
Bien, vamos a utilizar esta variable global en JavaScript: enTreasureConnectApp.vue
. Ahora mismo, los datos de user
siempre empiezan como null
. Podemos cambiarlo a window.user
:
... lines 1 - 26 | |
<script setup> | |
... lines 28 - 32 | |
defineProps(['entrypoint']); | |
const user = ref(window.user); | |
... lines 35 - 39 | |
</script> |
Cuando actualicemos... ¡ya está!
Siguiente: si utilizas Stimulus, una forma aún mejor de pasar datos a JavaScript es utilizar valores Stimulus.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^3.0", // v3.1.2
"doctrine/annotations": "^2.0", // 2.0.1
"doctrine/doctrine-bundle": "^2.8", // 2.8.3
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
"doctrine/orm": "^2.14", // 2.14.1
"nelmio/cors-bundle": "^2.2", // 2.2.0
"nesbot/carbon": "^2.64", // 2.66.0
"phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
"phpstan/phpdoc-parser": "^1.15", // 1.16.1
"symfony/asset": "6.2.*", // v6.2.5
"symfony/console": "6.2.*", // v6.2.5
"symfony/dotenv": "6.2.*", // v6.2.5
"symfony/expression-language": "6.2.*", // v6.2.5
"symfony/flex": "^2", // v2.2.4
"symfony/framework-bundle": "6.2.*", // v6.2.5
"symfony/property-access": "6.2.*", // v6.2.5
"symfony/property-info": "6.2.*", // v6.2.5
"symfony/runtime": "6.2.*", // v6.2.5
"symfony/security-bundle": "6.2.*", // v6.2.6
"symfony/serializer": "6.2.*", // v6.2.5
"symfony/twig-bundle": "6.2.*", // v6.2.5
"symfony/ux-react": "^2.6", // v2.7.1
"symfony/ux-vue": "^2.7", // v2.7.1
"symfony/validator": "6.2.*", // v6.2.5
"symfony/webpack-encore-bundle": "^1.16", // v1.16.1
"symfony/yaml": "6.2.*" // v6.2.5
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
"mtdowling/jmespath.php": "^2.6", // 2.6.1
"phpunit/phpunit": "^9.5", // 9.6.3
"symfony/browser-kit": "6.2.*", // v6.2.5
"symfony/css-selector": "6.2.*", // v6.2.5
"symfony/debug-bundle": "6.2.*", // v6.2.5
"symfony/maker-bundle": "^1.48", // v1.48.0
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/phpunit-bridge": "^6.2", // v6.2.5
"symfony/stopwatch": "6.2.*", // v6.2.5
"symfony/web-profiler-bundle": "6.2.*", // v6.2.5
"zenstruck/browser": "^1.2", // v1.2.0
"zenstruck/foundry": "^1.26" // v1.28.0
}
}