Una arquitectura de Single SignOn para todos los públicos

single signon architecture

El Inicio de Sesión Único (Single SignOn) es algo que vemos muy a menudo en empresas grandes con cientos de aplicaciones diferentes, pero que se nos antoja demasiado sofisticado para empresas de tamaño pequeño y mediano, donde esas aplicaciones se cuentan por decenas.

Este artículo presenta una arquitectura de Single SignOn que puede ser aplicada a cualquier escala, basada en código abierto y que ya ha sido probada en una implementación real.

Que es el Single SignOn

El Inicio de Sesión Único (mas conocido por el término inglés de Single SignOn) pretende unificar todas las aplicaciones de una organización bajo un mismo sistema de autenticación de usuarios, de forma que:

  • un usuario pueda usar las mismas credenciales en cualquiera de las aplicaciones; y
  • solo necesite presentar las credenciales una vez, sirviendo este inicio de sesión inicial para cualquier otra aplicación bajo el esquema de Single SignOn.

Una arquitectura de Single SignOn siempre va a disponer un elemento fundamental que es un Proveedor de Identidades (Identity Provider, IdP) común.

Los proveedores de identidades dispondrán de uno o varios protocolos para acceder a ellos, en nuestro caso vamos a usar un estándar abierto que se denomina OAuth y que es un mecanismo muy popular en todo tipo de aplicaciones web ya que es el que permite implementar lo que se denomina Social Login, que consiste en identificarnos en una web usando nuestros credenciales de un plataforma como puede ser Facebook, Twitter, Google … (consulta aquí la lista de los proveedores mas notables que soportan OAuth).

OAuth se basa en el uso de un token que define la autorización que tiene un sistema para acceder a la información de un usuario. OAuth permite compartir datos de un usuario sin necesidad de revelar sus credenciales. Si quieres saber mas sobre el funcionamiento de OAuth, te lo cuento en este artículo.

Un caso de uso

El caso de uso sobre el que queremos ilustrar la arquitectura de Single SignOn corresponde a una empresa de tamaño mediano con:

  • Diferentes páginas web, construidas sobre WordPress, cada una de las cuales implementa alguna función específica de la organización. Estas funciones son tanto públicas (para los clientes de la organización) , como privadas (para los empleados de la organización).
  • Una serie de tiendas especializadas creadas a partir de PrestaShop.
  • Una plataforma de aprendizaje basada en Moodle.

Cada una de estas plataformas (WordPress, PrestaShop y Moodle) proveen de un sistema propio de gestión de identidades y de autenticación de usuarios. Tan solo Moodle trae de serie un mecanismo para poder delegar estas funciones en un tercero.

Las organización quiere dar una visión integral y corporativa de sus servicios, pero el hecho que los usuarios tengan que gestionar un juego de credenciales diferentes en cada uno de sus nichos, no ayuda a este objetivo.

Una arquitectura de Single SignOn unida a una gestión centralizada de los datos personales del usuario puede ayudar a integrar todas las piezas de la organización.

A lo largo de su vida, cada uno de estos nichos ha ido registrando a una serie de usuarios de form independiente. Un paso fundamental de cara a poner en marcha una arquitectura de Single SignOn consiste en consolidar todas las bases de datos de usuarios en una única.

Arquitectura propuesta

Como ya he comentado la arquitectura dispondrá de un Proveedor de Identidades centralizado y estará basada en OAuth.

Es posible crear el Proveedor de Identidades como una pieza separada cuya única finalidad sea servir autorizaciones de acceso a los datos de usuario.

En nuestro caso particular, sin embargo, teníamos una web en la que residían ya un a inmensa mayoría de los usuarios totales de la organización. Además por la tipología de servicios que aporta esta web, todos los usuarios de la organización deben existir en su base de datos. Por tanto en nuestro caso particular era una buena idea hacer de este sistema en particular nuestro servidor central de identidades.

El Proveedor de Identidades debe convertirse en un servidor de OAuth. Los clientes de esta arquitectura implementarán la parte cliente de OAuth y además crearán una sesión local (en función de la plataforma) una vez que hayan obtenido la información de un usuario autenticado por el servidor.

También será necesario que el Proveedor de Identidades (que en nuestro caso es WordPress) cree una sesión local (a través de las cookies), de forma que sucesivas peticiones de autenticación del usuario por parte de diversos clientes no impliquen el volver a solicitar sus credenciales. Por supuesto, dicha sesión deberá expirar en algún momento (cuando expire la cookie de WordPress).

Proveedor de Identidades

El Proveedor de Identidades necesita implementar la parte servidora de OAuth. Para ello, y dado que estamos hablando de una web construida sobre WordPress, vamos a usar un plugin WP OAuth Server (OAuth Authentication) creado por WP OAuth Server.

La versión gratuita del plugin es suficiente para implementar la arquitectura propuesta, aunque debes revisar si la versión premium te sirve mejor en tu caso particular.

La configuración de este plugin es muy sencilla. Simplemente define credenciales para las aplicaciones clientes y habilita el servidor. El resto de los parámetros los puedes dejar con su valor por defecto.

Ampliando la identidad del usuario

La identidad de un usuario en WordPress es realmente escueta: ID, usuario (login), nombre a mostrar (display name), email, apodo (nicename), fecha de registro … y ya. Es posible que necesites algo mas y que tu web ya tenga alguno de los múltiples sistemas que existen para ampliar los datos de registro de usuario.

También es posible que necesites propagar la información sobre los roles a los que pertenece un usuario. En nuestro caso esta información es crucial para acceder a descuentos en las tiendas o para optar a cierto tipo de servicios bajo suscripción.

Para ampliar la información que tu servidor de OAuth va a facilitar a los clientes debes usar el mecanismo del que provee el plugin WP OAuth Server:

// Extiende los datos que facilita el proveedor de identidades
add_filter( 'wo_me_resource_return', function($me_data, $token) {
  $uid = $me_data['ID'];
  
  // Regular WP metadata
  $user_meta       = get_user_meta( $uid );
  $user_first_name = $user_meta['first_name'][0];
  $user_last_name  = $user_meta['last_name'][0];
  
  // Roles
  $user_data  = get_userdata( $uid );
  $user_roles = $user_meta->roles; 
  
  $extra_data = array();
  if ( !empty($user_first_name) ) $extra_data['first_name'] = $user_first_name;
  if ( !empty($user_last_name) )  $extra_data['last_name']  = $user_last_name;
  if ( count($user_roles) > 0 )   $extra_data['user_roles'] = $user_roles;
  
  return array_merge($me_data, $extra_data);
}, 10, 2);

El filtro wo_me_resource_return permite modificar el bloque de datos que se le enviará a los clientes de OAuth. En el ejemplo anterior añadimos al perfil del usuario la información del nombre, los apellidos y la lista de roles a los que pertenece. Esta información es estándar de WordPress. Además puedes añadir la que gestiones a través de tu sistema de extensión del perfil de usuario.

Este tipo de extensiones se añaden o bien a tu plugin personalizado para la web, si lo has creado (lo cuál recomiendo para casi cualquier web con WordPress), o al functions.php de tu tema.

Clientes de tipo WordPress

Para el resto de las web basadas en WordPress, hemos usado un plugin complementario al anterior que se denomina Simple Single Sign On creado por Dash10 Digital.

Este plugin es totalmente gratuito y no existe una versión premium.

La configuración del plugin es extremadamente sencilla. Debes facilitarle:

  • La URL del servidor de OAuth.
  • Los credenciales de acceso como cliente: Client ID y Client Secret, que previamente habrás creado en el servidor.

El plugin va a crear usuarios en la base de datos local de WordPress que son clones del usuario en el servidor central y les va a asignar una contraseña aleatoria (las credenciales se deben validar siempre contra el proveedor de identidades).

Los IDs no tienen por que coincidir, pero el login de usuario y los datos básicos se copian desde el servidor de identidades.

También debes tener en cuenta que la base de datos local de la web cliente solo tendrá los usuarios que alguna vez hicieron SignOn en ella, y no necesariamente una copia de todos los usuarios de tu proveedor central de identidades.

Usando la información ampliada

Para poder usar el perfil ampliado del usuario, también será necesario que extendamos este plugin. El siguiente ejemplo permite copiar la información de los roles de usuarios propagados por el proveedor de identidades, de forma que si un usuario local no tiene asignado uno de esos roles y el rol existe localmente, se le asigna.

/*
 * Copiar los roles del usuario que inicia sesión a través de Single SignOn.
 */
add_action( 'wpoc_user_login', function ( $user_info ) {
  return myplugin_copy_user_roles( $user_info );
} );
add_action( 'wpoc_user_created', function ( $user_info ) {
  return myplugin_copy_user_roles( $user_info );
} );

function myplugin_copy_user_roles( $user_info ) {

  if (empty($user_info)) return null;

  $roles = wp_roles();
  $user = get_user_by('login', $user_info->user_login);

  foreach ($user_info->user_roles as $role => $active) {
    if ($active) {
      if ($roles->is_role($role)) {          
         $user->add($role);
      }
    }
  }

  return true;
}

De la misma forma se podrían copiar otros datos de usuario que se hayan añadido al perfil de usuario.

Las acciones wpoc_user_created y wpoc_user_login ocurren en momentos distintos. La primera se refiere a la primera vez que el usuario hace login, cuando su usuario local en la web cliente aún no existe. La segunda hace referencia a las operaciones de SignOn sucesivas, cuando el clon del usuario local ya existe.

Clientes de tipo PrestaShop

Para PrestaShop no existía ningún modulo que simplemente implementase un cliente OAuth, aunque si hay muchos módulos que usan OAuth para realizar un social login. Por ello tuvimos que crear nuestro propio módulo PrestaShop para implementar el cliente de OAuth necesario para enganchar nuestras tiendas PrestaShop a la arquitectura de Single SignOn.

En este artículo se comenta de forma amplia cómo funciona y cómo se configura este plugin.

Clientes de tipo Moodle

Moodle es la única plataforma de las mencionadas que viene de serie con un cliente de OAuth. Por tanto para usar este mecanismo lo único que tenemos que hacer es activar y configurar nuestro cliente de OAuth.

Para habilitar y configurar el plugin de OAuth 2 debes ir a Administración > Extensiones > Identificación > Gestionar identificación.

Lo primero será habilitar el plugin OAuth 2:

La configuración se hace desde Administración > Servidor > Servicios OAuth 2 y debes crear un servicio custom. Debes indicar:

  • Nombre del servicio (el del servidor de identidades es una buena opción)
  • Client ID y Client Secret (de lo que hayas configurado en el servidor de identidades)
  • Activar Authenticate token requests via HTTP headers
  • Scopes included in a login request / Scopes included in a login request for offline access -> profile
  • Service base URL -> la URL de tu servidor de identidades
  • Logo URL -> Es opcional pero recomendable
  • Activar Show on login page
  • Desactivar Require email verification

No es necesario modificar la configuración de los endpoints ni conectar el servicio con una cuenta de sistema.

En la sección de mapeo de campos de cliente debes indicar como traducir los campos que llegan de tu proveedor de identidades con sus equivalentes en Moodle. Por ejemplo:

  • first_name --> firstname
  • last_name --> lastname
  • user_email --> email

Con esta configuración obtendremos un nuevo botón en la página de inicio de sesión que mostrará el nombre que le hayamos asignado al servicio y el logo.

Ocultar los sistemas nativos autenticación de usuarios

Con el esquema propuesto hasta aquí tenemos disponible un esquema de Single SignOn pero además tenemos los sistemas nativos de autenticación de usuarios de cada una de las webs.

En muchas ocasiones querremos que únicamente podamos hacer login en nuestras aplicaciones usando nuestro identidad del servidor central de identidades. Para ello debemos ocultar el sistema nativo de autenticación de usuarios en todos las aplicaciones bajo la arquitectura de SingleSigOn excepto en el propio Proveedor de Identidades.

También tendremos que modificar la forma en la que se recupera la contraseña del usuario o se accede al perfil del usuario para cambiar la contraseña o el resto de los datos del perfil, de forma que se redirija a esta función pero en el servidor central de identidades. Lo mismo ocurre con la función de registro de un nuevo usuario (Sign In), que deberá ser reconducida al registro en el Proveedor de Identidades.

Te explico como hacerlo en cada plataforma.

WordPress

En WordPress las acciones de login, registro y recuperación de contraseña se llevan a cabo en la página wp-login.php. Lo que vamos a hacer es crear nuestras propias páginas de inicio de sesión y registro.

/*
 * Evita usar las páginas de registro y de login de wordpress y en su lugar se redirige a los formularios de este plugin
 */
public function myplugin_custom_login() {
  global $pagenow;

  // Las peticione post, si se refieren a la página de login, son para hacer el propio login.
  if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) {

    $loginPage = get_bloginfo('url') . '/iniciar-sesion/';
    $signinPage = SSO_LOGIN_PROVIDER_URL . "/wp-login.php?action=register";

    // Redirigir a la página de registro del plugin
    if( 'wp-login.php' == $pagenow && $_GET['action'] == "register" ) {
        wp_redirect( $signinPage );
        exit();
    }

    // Redirigir a la página de login del plugin
    if( 'wp-login.php' == $pagenow && (empty($_GET['action']) || $_GET['action'] == "login") ) {
        wp_redirect( $loginPage );
        exit();
    }
  }
}
add_action( 'init', 'myplugin_custom_login' ) );

También te muestro un ejemplo de como crear una página de login personalizada:

Código de la página

<div class="my-login-wrapper" align="center">
  <div class="my-login-container">
  [sso_user_login login_title="Tengo cuenta de usuario en Mi Empresa" signin_title="Aún no tengo cuenta en Mi Empresa" signin_btn="yes" passwd_recovery_btn="yes"]
  </div>
</div>

Código del shortcode

add_shortcode( 'sso_user_login', function( $atts = NULL, $content = "" ) {
    // Parse atts
    $opt = shortcode_atts( array('signin_btn'              => 'no',
                                 'passwd_recovery_btn'     => 'no',
                                 'login_title'             => 'Iniciar sesión con mi cuenta de usuario',
                                 'login_btn_txt'           => 'Iniciar Sesión',
                                 'logout_title'            => 'Ya tienes una sesión iniciada.<br>¿No eres tu?',
                                 'logout_btn_txt'          => 'Cerrar Sesión',
                                 'signin_title'            => 'Crear una nueva cuenta de usuario',
                                 'signin_btn_txt'          => 'Crear una cuenta',
                                 'passwd_recovery_title'   => '¿Has olvidado tu contraseña?',
                                 'passwd_recovery_btn_txt' => 'Recuperar contraseña'
                               ), $atts );

    $sso_url           = get_site_url() . "/?auth=sso";
    $registration_url  = SSO_LOGIN_PROVIDER_URL . "/wp-login.php?action=register";
    $password_lost_url = SSO_LOGIN_PROVIDER_URL . "/wp-login.php?action=lostpassword";

    // If user is logged in, just show the logout button.
    if ( is_user_logged_in() ) {
      $output = "
      <div class='myplugin_login_login_area'>
        <div class='notice_wrapper'>
          <div class='alert alert-warning'>
            <span class='fa fa-info-circle' aria-hidden='true'></span> {$opt['logout_title']}
            <a class='my-end-session-link' href='/wp-login.php?action=logout'> {$opt['logout_btn_txt']}</a>
          </div>
        </div>
      </div>";
    } else {
      // No user logged in

      // Login button
      $output = "
      <div class='myplugin_login_form_shortcode'>
        <h4>{$opt['login_title']}</h4>
        <div class='myplugin_submit_btn'>
          <button type='button' class='btn btn-primary btn-lg btn_action'
                  onclick='location.href=\"{$sso_url}\"'>
        	  {$opt['login_btn_txt']}
          </button>
        </div>";

      // Password recovery button
      if ($opt['passwd_recovery_btn'] === 'yes') {
        $output .= "
        <div class='alert alert-warning'>
          <span class='fa fa-exclamation-triangle' aria-hidden='true'></span> {$opt['passwd_recovery_title']}
          <a class='my-end-session-link' href='{$password_lost_url}'>{$opt['passwd_recovery_btn_txt']}</a>
        </div>";
      }

      // Signin button
      if ($opt['signin_btn'] === 'yes') {
        $output .= "
        <h4>{$opt['signin_title']}</h4>
        <div class='myplugin_submit_btn'>
          <button type='button' class='btn btn-primary btn-lg btn_action'
                  onclick='location.href=\"{$registration_url}\"'>
        	  {$opt['signin_btn_txt']}
          </button>
        </div>";
      }

      // Closing tags
      $output .= "</div>";
    }

    // Return SSO login form HTML
    return $output . $content;
});

PrestaShop

En PrestaShop tendremos que modificar varias plantillas de nuestro tema para conseguir ocultar el proceso de autenticación nativo. Dado que cada tema está construido de una manera diferente, no es posible dar unas instrucciones concretas e infalibles, así que te cuento un ejemplo que tu deberás adaptar a tu tema en particular.

Las plantillas que ha habido que adaptar son:

  • \templates\customer\authentication.tpl
  • \templates\customer\my-account.tpl
  • \templates\customer\registration.tpl
  • \templates\customer\_partials\customer-form.tpl
  • \templates\_partials\form-fields.tpl
  • \templates\checkout\_partials\steps\personal-information.tpl
  • \templates\customer\password-email.tpl
  • \templates\customer\password-new.tpl

Plantilla authentication.tpl

Generalmente esta plantilla tiene dos secciones: un apara hacer login y otra para registrar una cuenta nueva.

En la parte de Login hay que remplazar:

{render file='customer/_partials/login-form.tpl' ui=$login_form}

por algo mas personalizado que use nuestro plugin de Single SignOn:

<div class="login_form">
     <div class="form_content">
		<div class="form_content_inner">
		  <div class="p-b-1">Acceda con su usuario registrado para <strong>Mi Empresa</strong> ({$oasso_oauth_server_name}).</div>
		</div>
     </div>
     <footer class="form-footer">
		{$HOOK_OASSO_CUSTOM nofilter}
     </footer>
</div>

En la parte de registro hay que remplazar:

<a href="{$urls.pages.register}" data-link-action="display-register-form">
	{l s='No account? Create one here' d='Shop.Theme.Customeraccount'}
</a>

por algo mas personalizado como por ejemplo:

<p><strong>Mi Empresa</strong> le permite tener un único usuario para todas nuestras webs. Si ya se ha registrado en otra web de <strong>Mi Empresa</strong> no es necesario que lo haga de nuevo; simplemente use sus credenciales para identificarse.</p>
<p>¿Es la primera vez que nos conoce? Crear una cuenta es rápido y sencillo. Con una cuenta puede comprar mas rápido y guardar sus pedidos.</p>
<a class="btn btn-primary btn-large js-btn-active btn-spin btn-full-width" href="https://{$oasso_oauth_server_name}/wp-login.php?action=register" data-link-action="display-register-form" id="SubmitCreate" rel="nofollow">
  <i class="fto-user icon_btn"></i>
  Crear una cuenta
</a>

Plantilla my-account.tpl

Aquí simplemente vamos a añadir una variable smarty al comienzo de la plantilla, para mas adelante saber que no estamos en un proceso de registro de usuario:

{assign var="is_registration_form" value=0 scope="global"}

Plantilla registration.tpl

Aquí tambien vamos a añadir algunas variables al comienzo de la plantilla:

{assign var="is_registration_form" value=1 scope="global"} 
{assign var="protect_form_fields" value=1 scope="global"} 

Y vamos a remplazar la forma en la que se muestra el formulario de registro de un nuevo usuario, remplazando:

<p>{l s='Already have an account?' d='Shop.Theme.Customeraccount'} <a href="{$urls.pages.authentication}">{l s='Log in instead!' d='Shop.Theme.Customeraccount'}</a></p>
{render file='customer/_partials/customer-form-regi.tpl' ui=$register_form}

Por algo similar a esto:

<div class="form_content">
  <div class="form_content_inner">
    <p>{l s='Already have an account?' d='Shop.Theme.Customeraccount'} <a href="{$urls.pages.authentication}" rel="nofollow" title="{l s='Log in instead!' d='Shop.Theme.Customeraccount'}">{l s='Log in instead!' d='Shop.Theme.Customeraccount'}</a></p>
    {$hook_create_account_top nofilter}
	<p><strong>Mi Empresa</strong> le permite tener un único usuario para todas nuestras webs. Si ya se ha registrado en otra web de <strong>Mi Empresa</strong> no es necesario que lo haga de nuevo; simplemente use sus credenciales para identificarse.</p>
	<p>¿Es la primera vez que nos conoce? Crear una cuenta es rápido y sencillo. Con una cuenta puede comprar mas rápido y guardar sus pedidos.</p>
  </div>
</div>
<footer class="form-footer text-center">
  <a href="https://{$oasso_oauth_server_name}/wp-login.php?action=register" class="btn btn-primary btn-large js-btn-active btn-spin btn-full-width" data-link-action="display-register-form" title="{l s='No account? Create one here' d='Shop.Theme.Customeraccount'}">
    <i class="fto-user icon_btn"></i>Crear una cuenta
  </a>
</footer>

Plantilla customer-form.tpl

Con Esta plantilla PrestaShop muestra los datos de registro del cliente y deja modificar algunos de ellos, entre los cuales está la contraseña. Lo que vamos a hacer es convertirla en una ventana puramente informativa, que incluya botones para modificar el perfil de usuario y cambiar la contraseña, que estarán enlazados con nuestro Proveedor de Identidades.

Al comienzo de la plantilla añadir:

{if !isset($is_registration_form)}{assign var=is_registration_form value=0}{/if}
{assign var="protect_form_fields" value=1 scope="global"}

En el bucle que va pintando los campos añadimos las líneas resaltadas:

{foreach from=$formFields item="field"}
  {* SSO: Elimina la posibilidad de modificar la contraseña*}
  {if $is_registration_form==0 && $field.name=='id_gender'}{continue}{/if}						
  {if $is_registration_form==0 && $field.name=='password'}{continue}{/if}				
  {if $is_registration_form==0 && $field.name=='new_password'}{continue}{/if}				
  {if $is_registration_form==0 && $field.name=='birthday'}{continue}{/if}				
  {block "form_field"}
    {form_field field=$field}
  {/block}
{/foreach}

Y al final del formulario remplazamos:

<input type="hidden" name="submitCreate" value="1">
{block "form_buttons"}
  <button class="btn btn-primary form-control-submit float-xs-right" data-link-action="save-customer" type="submit">
    {l s='Save' d='Shop.Theme.Actions'}
  </button>
{/block}

Por nuestros botones:

{block "form_buttons"}
  <a href="https://{$oasso_oauth_server_name}/perfil-de-usuario" class="btn btn-primary btn-large" data-link-action="display-register-form" title="Actualizar mis datos en Mi Empresa">
     <i class="fto-vcard-1 icon_btn"></i> Actualizar mi pefil de usuario
  </a>
  <a href="https://{$oasso_oauth_server_name}/modificar-password" class="btn btn-primary btn-large" data-link-action="display-register-form" title="Cambiar mi contraseña en Mi Empresa">
     <i class="fto-key icon_btn"></i> Cambiar mi contraseña
  </a>
{/block}

Referenciando las URLs concretas de nuestro servidor para perfil-de-usuario y modificar-password.

Plantilla form-fields.tpl

Esta plantilla se usa para mostrar cualquier campo de formulario en el front de PrestaShop, con lo que se usa de igual forma para mostrar los datos de un usuario (que no deben ser modificables) o para crear o editar una dirección de facturación o entrega.

En nuestro caso queremos que cuando se trate del formulario de datos de usuario, los campos no sean editables.

Al comienzo de la plantilla añadimos:

{if !isset($is_registration_form)}{assign var=is_registration_form value=0}{/if}
{if !isset($protect_form_fields)}{assign var=protect_form_fields value=0}{/if}

Para los campos de tipo radio-buttons, password y el tipo de campo por defecto, añadimos el siguiente código en la parte de atributos de la etiqueta <input>:

{if $protect_form_fields==1}disabled{/if}

Adicionalmente, para los campos de tipo password eliminamos:

<span class="input-group-btn">
  <button
    class="btn"
    type="button"
    data-action="show-password"
    data-text-show="{l s='Show' d='Shop.Theme.Actions'}"
    data-text-hide="{l s='Hide' d='Shop.Theme.Actions'}"
  >
    {l s='Show' d='Shop.Theme.Actions'}
  </button>
</span>

Y finalmente para el tipo de campo por defecto, modificamos:

  {if isset($field.availableValues.comment)}
    <span class="form-control-comment">
      {$field.availableValues.comment}
    </span>
  {/if}

condicionándolo de la siguiente forma:

{if !$is_registration_form==0}
  {if isset($field.availableValues.comment)}
    <span class="form-control-comment">
      {$field.availableValues.comment}
    </span>
  {/if}
{/if}

Plantilla personal-information.tpl

Esta plantilla tiene una función muy similar a authentication.tpl, pero durante el proceso de checkout. Los cambios son muy similares.

Remplazamos:

{if $guest_allowed}
  {l s='Order as a guest' d='Shop.Theme.Checkout'}
{else}
  {l s='Create an account' d='Shop.Theme.Customeraccount'}
{/if}

por el texto «Cliente nuevo».

Remplazamos:

{render file='checkout/_partials/customer-form.tpl' ui=$register_form guest_allowed=$guest_allowed}

por nuestro botón de registro:

<p><strong>Mi Empresa</strong> le permite tener un único usuario para todas nuestras webs. Si ya se ha registrado en otra web de <strong>Mi Empresa</strong> no es necesario que lo haga de nuevo; simplemente use sus credenciales para identificarse.</p>
<p>¿Es la primera vez que nos conoce? Crear una cuenta es rápido y sencillo. Con una cuenta puede comprar mas rápido y guardar sus pedidos.</p>
<footer class="form-footer">
  <a class="btn btn-primary btn-large js-btn-active btn-spin btn-full-width" href="https://{$oasso_oauth_server_name}/wp-login.php?action=register" data-link-action="display-register-form" id="SubmitCreate" rel="nofollow">
    <i class="fto-user icon_btn"></i>
    Crear una cuenta
  </a>
</footer>

También remplazamos:

{render file='checkout/_partials/login-form.tpl' ui=$login_form}

por nuestro botón de inicio de sesión:

<div class="form_content">
  <div class="form_content_inner">
    <p>Acceda con su usuario registrado para <strong>Mi Empresa</strong> ({$oasso_oauth_server_name}).</p>
  </div>
</div>
<footer class="form-footer">
  {$HOOK_OASSO_CUSTOM nofilter}
</footer>

Plantilla password-email.tpl

Se deben eliminar el contenido entre las etiquetas <header> y <section> e incluir algo similar a esto:

<p class="send-renew-password-link"><strong>Mi empresa</strong> le permite tener un único usuario para todas nuestras webs. Recuerde que sus credenciales (dirección de correo electrónico y contraseña) para esta web son los mismos que usa para <strong>Mi Empresa</strong> ({$oasso_oauth_server_name}).</p>
<p class="send-renew-password-link">Si no recuerda su contraseña puede solicitar que se le envíe un correo con las instrucciones para restablecerla.</p>
<footer class="form-footer flex_container">
  <a href="https://{$oasso_oauth_server_name}/wp-login.php?action=lostpassword" class="account-link btn btn-primary flex_child">
    <i class="fto-key icon_btn"></i> Restablecer mi contraseña Global
  </a>
</footer>

Plantilla password-new.tpl

A esta plantilla es prácticamente imposible llegar si hemos realizado los cambios anteriores. No obstante, en prevención de que alguien pudiera llegar a alcanzar la página, lo que haremos es eliminar todo el formulario.

Moodle

En Moodle no vamos a eliminar el resto de los métodos de autenticación (aunque en algunos casos sería viable). Los motivos para ello son por un lado que los administradores del sitio se autentican directamente contra el sistema nativo de Moodle y que por otro lado vamos a permitir que se hagan cursos como invitado. Por tanto vamos a disponer de tres formas de autenticarnos.

Lo que si que vamos a hacer es cambiar el orden de los controles en la ventana, que por defecto se vería:

dando prioridad al botón de single sign on:

También hemos ajustado algunos textos para remarcar que el login tradicional es solo para los administradores del sitio.

Para hacer esto hemos modificado una plantilla del tema de Moodle (login.mustache).

Se ha añadido un poco de CSS justo debajo de la etiqueta <body>, para reordenar los campos en la ventana:

<style>
#region-main > div:nth-child(3) > div.row.justify-content-center > div > div > div > div > div { display: flex }
#region-main > div:nth-child(3) > div.row.justify-content-center > div > div > div > div > div > div:nth-child(1) { order: 2 }
#region-main > div:nth-child(3) > div.row.justify-content-center > div > div > div > div > div > div:nth-child(2) { display: flex; flex-wrap: wrap }
#region-main > div:nth-child(3) > div.row.justify-content-center > div > div > div > div > div > div:nth-child(2) > h6 { order: -1 }
#region-main > div:nth-child(3) > div.row.justify-content-center > div > div > div > div > div > div:nth-child(2) > div.potentialidplist { order: -1; width: 100% }
</style>

Y al final de la plantilla se ha añadido un bloque de JavaScript:

<script>
var btn = document.getElementById('loginbtn');
if ( btn ) {
    btn.textContent = "Acceso Administrador";
}
var fld = document.getElementById('username');
if ( fld ) {
    fld.placeholder = "Administrador";
    fld.addEventListener('change', (event) => {
        if ( fld.value.includes('@') ) {
			alert('El formulario de inicio de sesión es solo para administradores del sitio.\n\nProfesores y alumnos deben usar el botón:\n    Identifíquese usando su cuenta en:\n    Mi Empresa');			
		}
    });	
}
</script>

Este JavaScript además de modificar algunos de los textos de etiquetas y botones nos lanza una alerta en el caso de que alguien trate de usar una dirección de correo en el campo username, lo que en nuestro caso implicaría un usuario que debería hacer login a través del botón de Single SignOn.