Wiki

ID Uruguay - Integración con OpenID Connect

(Esta es una una versión archivada de esta página. (1.0), Ir a la última versión.)
  1. ID Uruguay - OpenID Connect 1.0

===## Historial de revisiones
| Fecha      | Versión  | Descripción        | Autor      |
| -----------|:--------:| ------------------ |:----------:|
| 22/05/2018 | v0.1 | Creación de documento para intercambio e iteración | Christian Bouvier |
| 29/05/2018 | v0.2 | Se agrega sección ¿Por qué OpenID Connect 1.0 en ID Uruguay? | Christian Bouvier |
| 30/05/2018 | v0.3 | Se agrega infografía | Juan Martín Lusiardo |
| 05/06/2018 | v0.4 | Se actualiza lista de claims | Rodrigo Suárez |
| 14/06/2018 | v0.5 | Se actualiza esquema de flujo Authorization Code Flow. Se agrega descripción de cómo hacer refresh de un access token. | Christian Bouvier |
| 14/06/2018 | v0.6 | Se agrega parámetro nonce. | Victoria Cruces|
| 20/06/2018 | v0.7 | Se agrega parámetro prompt.| Victoria Cruces |
| 21/06/2018 | v0.8 | Se pasa a markdown versión validada | Christian Bouvier |

== Introducción ==

=== ¿Qué es ID Uruguay? ===
Es la plataforma de autenticación implementada por [AGESIC](https://www.agesic.gub.uy/) para centralizar cuentas de usuarios y facilitar el acceso web a los servicios digitales del Estado. Esto quiere decir que, una vez registrado en [ID Uruguay](https://mi.iduruguay.gub.uy/), un usuario podrá ingresar a los [servicios vinculados](https://mi.iduruguay.gub.uy/una-cuenta-para-todo) a la cuenta sin necesidad de nuevos registros ni contraseñas adicionales.

=== ¿Qué es OpenID Connect 1.0? ===
Es un protocolo de identidad simple y de estándar abierto creado sobre el protocolo [OAuth 2.0](https://tools.ietf.org/html/rfc6749), el cual permite a aplicaciones *Cliente* _(Relaying Party)_ verificar la identidad de un usuario basado en la autenticación realizada por este en un *Servidor de Autorización* _(OpenID Provider)_, así como también obtener información personal del usuario mediante el uso de una _API REST_.

=== ¿Por qué OpenID Connect 1.0 en ID Uruguay? ===
[OpenID Connect 1.0](http://openid.net/specs/openid-connect-core-1_0.html) es un protocolo que se encuentra implementado en múltiples lenguajes de programación y está siendo utilizado por la mayoría de los sitios y aplicaciones que requieren el manejo de un usuario, existiendo muchos proveedores (Google, Facebook, Linkedin, etc) y aún muchos más Clientes.

Su principal funcionalidad es permitir a usuarios de un sitio (Cliente), iniciar sesión o registrarse autenticandose en otro sitio (OpenID Provider) que ya contiene sus datos.

![Login with buttons](./assets/bootstrap-social.png)

Entre sus principales ventajas se encuentran:

=== Acelera el proceso de registro ===
La mayoría de los portales del estado (y otros sitios) solicitan a sus usuarios que completen un formulario de registro con información que, por lo general, comprende el mismo conjunto de datos (primer nombre, primer apellido, correo electrónico, ..etc). Con OpenID Connect, los usuarios pueden proveer esta información con un solo click. De este modo, el proceso de registro es más *simple, rápido y seguro.*

=== Reduce la frustración de administrar diferentes contraseñas ===
La mayoría de los usuarios que utilizan servicios web deben recordar diferentes nombres de usuario y contraseñas para iniciar sesión en los sitios que utilizan cada día. Recordar estas combinaciones puede ser una tarea muy tediosa, pero utilizar las mismas credenciales implica un problema de seguridad importante. Con OpenID Connect, es posible iniciar sesión o registrarte en todos estos sitios manteniendo solo una cuenta (la de *ID Uruguay*).

=== Mejora el control sobre la identidad electrónica ===
Cada vez que un usuario inicia sesión en un aplicación externa utilizando OpenID Connect, deberá dar su consentimiento explícito de que datos quiere compartir con la aplicación. De este modo, su cuenta ID Uruguay servirá como una identidad centralizada que podrá utilizarse de manera controlada en muchos sitios de internet.

=== Minimiza los riesgos de seguridad asociados a las contraseñas ===
Muchos usuarios utilizan la misma contraseña en múltiples sitios. De este modo, si alguno de estos fuera atacado y las contraseñas quedaran comprometidas, un _hacker_ podría obtener acceso a todos los sitios que comparten esta credencial. Con OpenID Connect, si ocurriera una situación que comprometa las credenciales, basta con restablecer la contraseña en un lugar, *ID Uruguay*.

== Objetivo ==
El objetivo de este documento es brindar una descripción detallada de cómo funciona el protocolo OpenID Connect 1.0 en ID Uruguay, y como es posible operar como un cliente del mismo para obtener información verificada de los ciudadanos, de manera segura, tanto para la aplicación cliente como para los usuarios.

== Terminología ==
La terminología utilizada en este documento responde a la detallada por las especificaciones de [OpenID Connect 1.0](http://openid.net/specs/openid-connect-core-1_0.html#Terminology) y [OAuth 2.0](https://tools.ietf.org/html/rfc6749). No obstante, se brindarán definiciones para algunos términos fundamentales como los roles, claims y tokens.

La terminología utilizada en este documento responde a la detallada por las especificaciones de [OpenID Connect 1.0](http://openid.net/specs/openid-connect-core-1_0.html#Terminology) y [OAuth 2.0](https://tools.ietf.org/html/rfc6749). No obstante, se brindarán definiciones para algunos términos fundamentales como: *roles, claims, scopes y tokens.*

== Roles ==
Los roles que participan en un flujo de autenticación OpenID Connect 1.0 son los siguientes:

*End-User:* Participante humano capaz de autorizar el acceso a un recurso protegido validando su identidad.

*Relaying Party (RP):* Aplicación Cliente que solicita la autenticación de un End-User a un OpenID Provider, con el fin de poder acceder a recursos protegidos en nombre del usuario autenticado. En este caso, será la aplicación web/mobile que quiere integrarse con ID Uruguay para obtener información del usuario (claims).

*OpenID Provider (OP):* Servidor de Autenticación capaz de autenticar usuarios y proveer información sobre estos y el proceso de autenticación a un Relying Party. En este caso, será ID Uruguay.

== Descripción general ==
En términos generales, el protocolo *OpenID Connect 1.0* en *ID Uruguay* se puede describir a través de los pasos listados a continuación:

1. Un Cliente registrado (RP) envía un pedido de autenticación a ID Uruguay (OP).
2. El OP autentica al usuario y obtiene su autorización para compartir ciertos datos (claims) con el RP.
3. El OP responde con un ID Token y un Access Token al RP.
4. El RP utiliza el Access Token para solicitar información sobre el usuario al UserInfo Endpoint.
5. El UserInfo Endpoint retorna un listado de claims del usuario.

*Finalmente, la aplicación Cliente (RP) podrá crear una sesión para el usuario autenticado o registrarlo en su base de datos con los claims obtenidos.*

![Flujo Abstracto](./assets/abstract-autorization-flow.png)

> NOTA: La autenticación en OpenID Connect puede llevarse a cabo de tres formas: _Authorization Code Flow, Implicit Flow o Hybrid Flow_. El mecanismo seleccionado determina como el _ID Token_ y _Access Token_ son retornados al Cliente, y esto puede hacer que ciertos pasos sean modificados y/o nuevos pasos añadidos. En particular en *ID Uruguay* se implementa el _Authorization Code Flow_, que será descrito en detalle más adelante.

== Tokens ==
En esta sección se presentan los diferentes _tokens_ que participan en el proceso de autenticación y autorización de OpenID Connect 1.0, y por lo tanto en la implementación de ID Uruguay.

=== ID Token ===
Un _ID token_ es un [JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7523) que contiene información sobre el proceso de autenticación del _End-User_ en el servidor de autenticación (OP).

=== Access Token ===
Los _Access tokens_ son credenciales emitidas por el servidor de autenticación (OP) para un cliente (RP), y tienen como fin permitir a estos últimos el acceso a recursos protegidos. Un _Access Token_ es un _string_ _opaco_ que representa el acceso a ciertos datos y puede ser utilizado por un tiempo limitado.

=== Refresh Token ===
Los _Refresh tokens_ son credenciales emitidas por el servidor de autenticación (OP) para un cliente (RP), y tienen como fin la obtención de nuevos _Access tokens_, cuando estos expiran o se vuelven inválidos.

== Claims y Scopes ==
Los _JWT_ contienen _claims_, campos de información (tales como nombre o e-mail) sobre una entidad (típicamente un usuario) y metada adicional.

OpenID Connect 1.0 define un conjunto standard de _claims_, que incluyen nombre, email y género entre otros. Y los agrupa en _scopes_, que permiten a un RP definir que tipo de información desea obtener sobre un usuario.

Por su parte, ID Uruguay toma como base los _claims_ y _scopes_ de OpenID Connect 1.0 y define los siguientes, que podrán ser solicitados en un _Authentication Request_:

Nombre         Claims       Descripción      
------------- :-----------: :----------------
personal_info primer_nombre, segundo_nombre, primer_apellido, segundo_apellido, nivel_de_confianza Nombres y apellidos del usuario, y el nivel de confianza. Este último puede ser alguno de los siguientes valores: AUTOREGISTRADO, PRESENCIAL, CERTIFICADO
document       pais_documento, tipo_documento, numero_documento Información sobre el documento del usuario
email         email, email_verified Correo electrónico y si el mismo está verificado.

De este modo, un RP podrá obtener los datos incluídos en los _scopes_ que solicite, previa autorización del _End-User_ (propietario de dichos datos).

## Integración OpenID Connect con ID Uruguay
El objetivo de esta sección es describir el procedimiento para registrarse y poder operar como _Relaying Party_ en el proceso de autenticación/autorización OpenID Connect 1.0 de ID Uruguay.

=== Registro como Cliente _(Relaying Party)_ ===
[PENDIENTE AGESIC]

=== Autenticación utilizando _Authorization Code Flow_ ===
El _Authorization Code Flow_ es uno de los tres posibles flujos de autenticación provistos por el protocolo OpenID Connect 1.0. En este, un RP redirige al _End-User_ al _Authorization Endpoint_ del OP, el cual lleva a cabo la autenticación y autorización del mismo. Si el resultado es exitoso, el OP retorna al RP un _Authorization Code_, que podrá ser utilizado (dentro de un período de tiempo) para obtener un _ID Token_ y _Access Token_ desde el _Token Endpoint_. Finalmente, el _Access Token_ obtenido puede ser utilizado para conseguir _claims_ sobre el usuario en el _Userinfo Endpoint_. El diagrama a continuación ilustra el flujo descrito

![Esquema Autorization Code Flow](./assets/autorization-flow.png)

> NOTA: Este flujo de autenticación tiene como beneficio que ningún token se encuentra expuesto al User Agent, y de este modo, a posibles aplicaciones maliciosas que lo controlen. Por ello, es apropiado para Clientes que puedan mantener de manera segura una clave secreta, típicamente aplicaciones con un Backend de datos.

=== Authorization Endpoint (/oidc/authorize) ===
El _Authorization Endpoint_ lleva a cabo la autenticación y autorización del _End-User_. Para invocarlo se debe enviar un _Authentication request_, que será respondido por un _Authentication response_. Ambos pedidos son detallados a continuación.

==== Authentication request ====
Un _Authentication request_ es un pedido HTTP con ciertos parámetros, que sirve para solicitar la autenticación de un _End-User_ en ID Uruguay.

Este pedido puede llevarse a cabo empleando los métodos HTTP GET o HTTP POST definidos en el [RFC 2616](https://tools.ietf.org/html/rfc2616). Si se utiliza el método HTTP GET, los parámetros deberán ser serializados empleando [URI Query String Serialization](https://tools.ietf.org/html/rfc6570). En caso de utilizar el método HTTP POST, los parámetros deberán ser serializados utilizando [Form Serialization](https://www.w3.org/TR/2003/REC-xforms-20031014/slice11.html).

A continuación se muestra una tabla con los parámetros aceptados y una breve descripción de cada uno:

Parámetro     Tipo       Descripción  
------------- :---------: ------------:
scope         Requerido Siempre debe incluirse `openid`. Adicionalmente se pueden incluir los scopes descritos en la sección *Claims y Scopes* separados por espacios. Ej: `openid personal_info email`
response_type Requerido Valor que determina el tipo de flujo de autenticación a utilizar. En caso del _Authorization Code Flow_, es valor es `code`.  
client_id     Requerido Identificador del cliente provisto al momento del registro
redirect_uri   Requerido URI a donde debe ser enviada la respuesta. La misma debe ser una de las registradas al momento de darse de alta como cliente.
state         Recomendado Valor opaco para mantener el estado entre el pedido y la respuesta. Será retornado al cliente junto con el código de autorización o error
nonce         Opcional String opaco utilizado para asociar la sesión de un Cliente con un _ID Token_, y mitigar _replay attacks_.
prompt         Opcional Lista de valores de cadena ASCII delimitados por un espacio, sensibles a minúsculas y mayúsculas, que especifica si el servidor de autorización solicita al usuario final la reautenticación y consentimiento. Los valores definidos son: `none, login y consent.`

==== Ejemplo de _Authentication request_ con HTTP GET ====
El RP retornará un HTTP 302 Redirect.
{{{HTTP/1.1 302 Found
  Location: https://auth-testing.iduruguay.gub.uy/oidc/authorize?
    response_type=code
    &scope=openid%20personal%20email
    &client_id=ID_CLIENTE
    &state=STRING_RANDOM
    &redirect_uri=https%3A%2F%2Fclient.org%2F
}}}
El navegador realiza el pedido:
{{{GET /oidc/authorize?
    response_type=code
    &scope=openid%20personal%20email
    &client_id=ID_CLIENTE
    &state=STRING_RANDOM
    &redirect_uri=https%3A%2F%2Fclient.org%2F
  Host: https://auth-testing.iduruguay.gub.uy
}}}
==== Ejemplo de _Authentication request_ con HTTP POST ====
{{{POST /oidc/authorize HTTP/1.1
Host: auth-testing.iduruguay.gub.uy
Content-Type: application/x-www-form-urlencoded

response_type=code&scope=openid%20personal%20email&client_id=ID_CLIENTE&state=STRING_RANDOM&redirect_uri=https%3A%2F%2Fclient.org%2F
}}}
==== Authentication Response ====
El _Authentication Response_ es el mensaje retornado por el _Authorization Endpoint_ del OP en respuesta a un _Authentication request_ enviado por el RP.

La respuesta incluye los parámetros `code` y `state`, codificados con el formato _"application/x-www-form-urlencoded"_ y añadidos a la *redirect_uri* especificada al enviar el _Authentication request_. La tabla a continuación presenta una breve descripción de los parámetros mencionados:

Parámetro     Tipo       Descripción  
:-------------::---------: ------------:
code Requerido Código de autorización generado por el OP. Puede ser utilizado una única vez para obtener un _ID Token_ y _Access Token_. Expira en 10 minutos.
state Requerido si fue envíado El valor exacto recibido del RP en el parámetro `state` del _Authentication Request_.

==== Ejemplo de respuesta exitosa ====
{{{HTTP/1.1 302 Found
 Location: https://client.org/?
   code=SplxlOBeZQQYbYS6WxSbIA
   &state=STRING_RANDOM
}}}
Si el _request_ falla debido a que el parámetro *redirect_uri* es vacío, inválido o no coincide con ninguna de las URI de redirección configuradas al momento del registro, o si el parámetro *client_id* es vacío o inválido, el OP mostrará al _End-User_ una pantalla de error y no redireccionará el _User-Agent_ a la URI inválida.

![Pantalla de error](./assets/error-screen.png)

Por otra parte, si el _End-User_ rechaza el request o si la autenticación falla por razones diferentes a las antes mencionadas, el OP informa al RP añadiendo a la URI especificada por el parámetro *redirect_uri*, los siguientes parámetros codificados con el formato _"application/x-www-form-urlencoded"_:

Parámetro     Tipo       Descripción  
:-------------::---------: ------------:
error Requerido Un código de error de los descritos en [OpenID Connect 1.0](http://openid.net/specs/openid-connect-core-1_0.html#AuthError) u [OAuth 2.0](https://tools.ietf.org/html/rfc6749#section-4.1.2)
error_description Opcional Descripción del error que provee información para ayudar a los desarrolladores a entender el error ocurrido.
state Requerido si fue envíado El valor exacto recibido del RP en el parámetro `state` del _Authentication Request_.

==== Ejemplo de respuesta con error ====
{{{HTTP/1.1 302 Found
  Location: https://client.org/?
    error=invalid_request
    &error_description=
      Unsupported%20response_type%20value
    &state=STRING_RANDOM
}}}
=== Token Endpoint (/oidc/token) ===
Para obtener un _ID Token_, un _Access Token_ y opcionalmente un _Refresh Token_, el RP envía un _Token Request_ al _Token Endpoint_ del OP, quien responderá con un _Token Response_.

==== Token Request ====
Un RP realiza un _Token Request_ presentando su código de autorización (`code`) ante el _Token Endpoint_ del OP. Este _request_ debe implementarse utilizando el método HTTP POST, y debe contener los siguientes parámetros serializados como Form Serialization:

Parámetro     Tipo       Descripción  
:-------------::---------: ------------:
grant_type Requerido Tipo de credenciales a presentar. Debe ser `authorization_code`
code Requerido Código de autorización emitido por el OP, previamente tramitado en el _Authentication Endpoint_
redirect_uri Requerido URI a donde debe ser redirigido el _User Agent_ con la respuesta (_Token Response_). Debe ser una de las URIs configuradas al momento del registro del RP

Adicionalmente a estos parámetros, el _Token Request_ debe contener las credenciales de autenticación provistas al momento del registro (*client_id y client_secret*) siguiendo el esquema de autenticación [HTTP Basic Auth](https://tools.ietf.org/html/rfc7617).

==== Ejemplo de Token Request HTTP POST ====
Si `client_id = 123456789` y `client_secret = 0Pg8RabLluvuoG3`, un ejemplo de _Token Request_ es:
{{{POST /token HTTP/1.1
Host: https://auth-testing.iduruguay.gub.uy
Content-Type: application/x-www-form-urlencoded
Authorization: Basic MTIzNDU2Nzg5OjBQZzhSYWJMbHV2dW9HMw==

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
  &redirect_uri=https%3A%2F%2Fclient.org%2F
}}}
En caso de que se quiera obtener un nuevo Access Token (Refresh), se puede utilizar el Token Endpoint enviando un Token Request que con los parámetros:

Parámetro     Tipo         Descripción  
:------------- :---------:   ------------:
grant_type   Requerido   Tipo de credenciales a presentar. Debe ser `refresh_token`
refresh_token   Requerido   `refresh_token` obtenido en el _ID Token_ anterior.

==== Token Response ====
Luego de recibido y validado un _Token Request_ del RP, el OP procede a responder con un _Token Response_ que incluye los siguientes parámetros codificados como `application/json`:

Parámetro     Tipo         Descripción  
:------------- :---------:   ------------:
access_token   Requerido   _Access Token_ emitido por el OP
token_type     Requerido   Tipo de token. Será siempre `Bearer`
id_token       Requerido   _ID Token_ asociado a la sesión de autenticación
expires_in     Recomendado Tiempo de vida del _Access Token_ en segundos. Valor por defecto 60 minutos
refresh_token Opcional     _Refresh Token_ que puede ser utilizado para obtener nuevos _Access Tokens_

==== Ejemplo de _Token Response_ exitoso ====
{{{HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
 "access_token": "bf6ab0e8fcef4ec7be5cfbfecb520c7f",
 "token_type": "bearer",
 "refresh_token": "6859d02ddb794e66b71321b587046344",
 "expires_in": 3600,
 "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc
   yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5
   NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ
   fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz
   AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q
   Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ
   NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd
   QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS
   K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4
   XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"
}
}}}
> NOTA: Para evitar que el _User-Agent_ almacene en caché información sensible. El _Token Response_ incluye los Headers HTTP `Cache-Control: no-store` y `Pragma: no-cache`

Si el _Token Request_ es inválido o no pudo ser autorizado, el OP crea una respuesta con los siguientes parámetros codificados como `application/json`, y un código de error `HTTP 400 Bad Request`:

Parámetro     Tipo       Descripción  
:-------------::---------: ------------:
error Requerido Un código de error de los descritos en [OAuth 2.0](https://tools.ietf.org/html/rfc6749#section-5.1)
error_description Opcional Descripción del error que provee información para ayudar a los desarrolladores a entender el error ocurrido.

=== Userinfo Endpoint (/oidc/userinfo) ===
El _UserInfo Endpoint_ es un _endpoint_ protegido que retorna _claims_ sobre el _End-User_ autenticado. Para obtener estos _claims_, el RP envía un _UserInfo Request_ acompañado por un _Access Token_ obtenido a través del flujo de autenticación OpenID Connect. Los _claims_ se retornan como clave-valor en la respuesta con formato JSON.

==== UserInfo Request ====
Un _UserInfo Request_ puede ser implementado utilizando los métodos HTTP GET y HTTP POST, y en cualquier caso deberá incluir el header HTTP `Authorization` (definido por [HTTP 1.1](https://tools.ietf.org/html/rfc2617)) siguiendo el esquema `Bearer` ([OAuth 2.0  Bearer Token Usage](https://tools.ietf.org/html/rfc6750)) para incluir el _Access Token_ previamente obtenido.

==== Ejemplo de _UserInfo Request_ HTTP GET ====
Si `SlAV32hkKG` es el _Access Token_ obtenido, un UserInfo Request podría ser:
{{{GET /userinfo HTTP/1.1
Host: https://auth-testing.iduruguay.gub.uy
Authorization: Bearer SlAV32hkKG
}}}
==== UserInfo Response ====
El _UserInfo Response_ se retorna como respuesta a un _UserInfo Request_, y devuelve los _claims_ solicitados como atributos de un JSON. Por razones de privacidad, el OP puede elegir no retornar alguno de los _claims_ solicitados (si por ejemplo no fue acordado al momento del registro).

El _UserInfo Response_ siempre incluirá el campo `sub` (subject). Este, DEBE ser exactamente igual al campo `sub` del ID Token recibido. Si no coinciden, la respuesta debe considerarse inválida.

==== Ejemplo de _UserInfo Response_ exitoso. ====
{{{HTTP/1.1 200 OK
Content-Type: application/json
{
  "sub": "248289761001",
  "email": "juan@email.com",
  "primer_nombre": "Juan",
  "segundo_nombre": "José",
  "primer_apellido": "Perez",
  "segundo_apellido": "Martinez"
}
}}}
Si ocurre algún tipo de error, el _UserInfo Endpoint_ retorna un _Error Response_ tal y como lo especifica [OAuth 2.0  Bearer Token Usage](https://tools.ietf.org/html/rfc6750)

==== Ejemplo de _UserInfo Response_ con error. ====
{{{HTTP/1.1 401 Unauthorized
  WWW-Authenticate: error="invalid_token",
    error_description="The Access Token expired"
}}}
== Referencias ==
- [OpenID Connect 1.0](http://openid.net/specs/openid-connect-core-1_0.html)
- [OAuth 2.0](https://tools.ietf.org/html/rfc6749)
- [JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7523)
- [JWT](https://jwt.io/)

27713 Accesos
Promedio (0 Votos)