thumbnail img ios design authentication

ios - img - Patrón de diseño de expiración de sesión



ionic navbar image (6)

A menudo tuve que lidiar con los tokens de sesión / acceso que caducaban en un diseño de aplicación de iOS y nunca encontré un diseño con el que me sintiera 100% cómodo, así que estoy preguntando esto aquí para ver si alguien puede diseñar un mejor diseño. de lo que uso actualmente.

El problema

Usted tiene una aplicación que inicia sesión con un nombre de usuario y contraseña. El servidor devuelve un token de acceso que se debe usar para futuras solicitudes para autenticar a ese usuario. En algún momento en el futuro (hora desconocida), el servidor caducará ese token y cualquier solicitud enviada con ese token devolverá un error de autenticación.

Después del error debido a que la sesión expira, la aplicación debe volver a iniciar sesión con las credenciales originales y obtener un token de acceso nuevo. Luego puede volver a intentar la solicitud original.

Ejemplo

Imagine que tiene una API para obtener una lista de artículos de noticias que requieren autenticación. El flujo puede ser así:

  1. El usuario inicia sesión y la aplicación recibe el token.
  2. El controlador de vista actualiza la lista de artículos de noticias.
    1. La solicitud API se realiza con el token adjunto.
    2. La solicitud de API fue exitosa y la vista se actualiza con nuevos artículos.
  3. La aplicación está cerrada y pasa algún tiempo.
  4. La aplicación se abre y el controlador de la vista desea actualizar la lista de artículos de noticias.
    1. La solicitud API se realiza con el token adjunto.
    2. La solicitud de la API no tiene éxito porque token ha caducado.
    3. El controlador de vista solicita una actualización del token y espera con paciencia.
    4. Una vez que se ha actualizado el token, se vuelve a intentar la solicitud de API.

Ahora imagina que esto se hace desde más de un lugar en la aplicación.

Cómo lo resuelvo actualmente

Lo que suelo hacer es almacenar las credenciales en NSUserDefaults (si no me preocupa la seguridad de esas credenciales, obviamente es mejor utilizar el llavero) y luego tener un método en un objeto de administrador global (singleton) que refresque el inicio de sesión usando estas credenciales cuando noto que la sesión ha expirado. Este administrador global activa las notificaciones cuando el estado de inicio de sesión cambia para que otras partes de la aplicación puedan saber cuándo deben volver a intentar una solicitud después de la falla debido a la caducidad de la sesión.

Lo que no siento es correcto

Bueno, nunca me gustó el manejo de la máquina de estado del objeto administrador. Cada lugar que realiza una solicitud necesita guardar un estado para saber que está en curso una actualización de inicio de sesión y volver a intentar la solicitud después de que se haya actualizado el inicio de sesión. También existe el problema de qué hacer si falla la actualización porque la contraseña es incorrecta ahora (el usuario la cambió): es probable que no desee desconectarse por completo y destruir todo el estado de usuario de la aplicación, porque puede que solo sea capaz de pedir la nueva contraseña y continuar como antes. Pero el gerente global no se relaciona realmente con la interfaz de usuario, por lo que es difícil manejar la interfaz de usuario para solicitar el nuevo inicio de sesión.

Lo que quiero saber en las respuestas

Entiendo que esta pregunta es particularmente vaga y conceptual (¿aún así creo que está bien estar en StackOverflow?), Pero realmente me gustaría saber cómo otras personas resuelven este tipo de problema. Solo una explicación de cómo maneja la caducidad de la sesión, reintentando las solicitudes fallidas de toda la aplicación y pidiendo al usuario nuevas credenciales si la actualización no funcionó.

Creo que el quid de la cuestión es esta:

Dónde colocar la lógica de reintento de las solicitudes que fallaron debido a la caducidad de la sesión. Veo que estos lugares son opciones:

  1. En el nivel de controlador de vista (es decir, mantener una bandera para decir que tenemos que volver a intentar la última solicitud una vez que haya finalizado la actualización de inicio de sesión).
  2. En el nivel de solicitud de API (es decir, encapsula la solicitud de API en un objeto que sabe volver a intentar después de que se haya actualizado el inicio de sesión).
  3. En el nivel de administrador de inicio de sesión global (es decir, tal vez tome un bloque cuando se refreshLogin que se ejecuta una vez que se ha actualizado el inicio de sesión).

Desde mi punto de vista, el lugar correcto para poner la lógica es en el nivel del Controlador de Vista.

Si entendí correctamente su pregunta, tiene una API de red que maneja las llamadas al servidor y devuelve el resultado (posiblemente desde un JSON) al controlador de vista.

El mejor enfoque debería ser crear un Controlador de vista de inicio de sesión, con los campos de nombre de usuario / correo electrónico y contraseña, separados del resto de la lógica de la aplicación. Después de recibir el OK del servidor, este controlador de vista se puede descartar y la aplicación fluirá según lo previsto.

Si su token fue invalidado, el servidor debería devolver un error 401 a su API de red. Simplemente puede encapsular ese error en un objeto NSError y pasarlo a View Controller para que lo maneje.

En cualquier lugar de la aplicación, una llamada al servidor puede devolver el error 401 y, como resultado, se disculpa con su usuario y retira el Controlador de vista de inicio de sesión para forzar la creación de un nuevo token.

Espero haber ayudado.


Enfrentando el mismo problema desde hace un par de meses. Mi enfoque ''(2) En el nivel de solicitud API''

Por favor, avíseme si obtuvo la mejor solución para resolver esto. Eso realmente ayudará a muchas personas que enfrentan estos problemas.

actualizar el token de autenticación cuando la sesión caduque es una solución muy grande.


Lo que has preguntado es cómo lidiar con la caducidad de la sesión. Los otros han respondido tu pregunta. Pero creo que estás haciendo la pregunta incorrecta. Dejame explicar.

Lo que queremos es un patrón de diseño para las sesiones de usuario en iOS. Necesitamos verlo desde el punto de vista del dispositivo iOS:

  1. El usuario instala la aplicación
  2. El usuario autentica con detalles y se devuelve un token de sesión
  3. La aplicación almacena el token de sesión (secreto) en el dispositivo
  4. Cualquier solicitud futura incluye el token de sesión

Conclusión: cualquier API diseñada para iOS no debe caducar el token de sesión. Simplemente se mantiene en secreto en el dispositivo.

Por lo tanto, según mi experiencia, la respuesta a su pregunta sobre un patrón de diseño para la expiración de la sesión es básicamente: No use la caducidad de la sesión cuando use una API para iOS.

Una de las mayores API REST de iOS lo está haciendo de esta manera, y yo tendría que estar de acuerdo.


Mi enfoque es similar a su ''(2) En el nivel de solicitud API'', excepto que en lugar de encapsular la solicitud en un objeto, encapsula todo el concepto del servidor API.

Por lo tanto, generalmente termino envolviendo cada tipo de solicitud con una firma de método asíncrono como esta:

(Lo siento, es C #, no obj-c (yo uso Xamarin.iOS) pero el concepto es el mismo - Action<T> es equivalente a obj-c Blocks )

void GetLatestNews(GetLatestRequest request, Action<NewsListingResponse> success, Action<Exception> error) void Search(SearchRequest request, Action<SearchResponse> success, Action<Exception> error)

Normalmente solo hago estos métodos estáticos en una clase estática (muy raramente habrá más de 1 servidor del mismo tipo, por lo que una sola clase estática funcionará bien) pero a veces los pongo dentro de una clase de instancia singleton para que pueda pasar en una instancia burlada para pruebas unitarias.

De todos modos, ahora su código de cliente de VC solo consume la API, según sea necesario:

override void ViewDidAppear(bool animated) { SomeNewsSiteApi.GetLatestNews( new GetLatestRequest{Count=20}, response => { // Update table view here }, error => { // Show some error alert }); }

La belleza de esto es que la implementación de esos métodos maneja el envío de la solicitud con el token actual, y si falla, obtiene un token nuevo y luego reenvía la misma solicitud con el token nuevo, y finalmente vuelve a llamar a la Action<T> success devolución de llamada. El código de VC del cliente no tiene idea acerca de la re-obtención de tokens, lo único que sabe y lo que más le preocupa es que está solicitando algunos datos y esperando una respuesta o un error.


No para plantear esta pregunta de entre los muertos, pero como no ha sido respondida, daré mi opinión.

Lo que elegí hacer para este escenario fue almacenar las credenciales en el llavero (o donde sea, en realidad), y luego subclasé un HTTPClient para verificar si se actualizaba o no antes de cada llamada. De esta forma, puedo identificar una actualización, realizarla y volver a intentar la llamada en un solo paso, así como enviar un bloque de error a la cadena si es necesario para manejar cualquier caso en el que el usuario NO pueda actualizarse en consecuencia .

Esto parece estar en línea con lo que estás (o probablemente estuviste) tratando de lograr. No hay necesidad de notificaciones ni de ningún otro tipo de jazz, y puede escribirlo una vez y volver a utilizarlo en toda la aplicación enviando sus llamadas a través de ese cliente HTTP subclasificado.

EDITAR: Tenga en cuenta que debe recordar permitir que pase cualquier llamada de autenticación.


Usted menciona dos cosas en su pregunta que parecen claves para responderla:

A) la aplicación está en un estado con datos que podrían perderse si el usuario no está conectado.
B) la aplicación debe volver a iniciar sesión para guardar los datos.

Si estas son las limitaciones, debe trabajar en consecuencia en consecuencia:

  1. Diseñe un esquema en el que pueda guardar cambios localmente. Mantenga un registro del estado de sincronización con el servidor.
  2. Si el estado de inicio de sesión cambia y sucede algo inesperado, alerta discretamente al usuario y dale la oportunidad de solucionarlo.