Commit c6908b87 authored by Xavier Guimard's avatar Xavier Guimard

Yubikey 2nd factor (closes: #1399)

parent 396fc236
......@@ -129,7 +129,7 @@
.\" ========================================================================
.\"
.IX Title "llng-fastcgi-server 1"
.TH llng-fastcgi-server 1 "2018-03-14" "perl v5.26.1" "User Contributed Perl Documentation"
.TH llng-fastcgi-server 1 "2018-03-20" "perl v5.26.1" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
......
......@@ -247,9 +247,11 @@ sub defaultValues {
'totp2fDigits' => 6,
'totp2fInterval' => 30,
'totp2fRange' => 1,
'totp2fSelfRegistration' => 0,
'trustedProxies' => '',
'twitterAuthnLevel' => 1,
'u2fActivation' => 0,
'u2fSelfRegistration' => 0,
'u2fUserCanRemoveKey' => 1,
'upgradeSession' => 1,
'userControl' => '^[\\w\\.\\-@]+$',
......@@ -260,6 +262,9 @@ sub defaultValues {
'webIDAuthnLevel' => 1,
'webIDExportedVars' => {},
'whatToTrace' => 'uid',
'yubikey2fActivation' => 0,
'yubikey2fPublicIDSize' => 12,
'yubikey2fSelfRegistration' => 0,
'yubikeyAuthnLevel' => 3,
'yubikeyPublicIDSize' => 12
};
......
......@@ -3229,7 +3229,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
},
'totp2fSelfRegistration' => {
'default' => 0,
'type' => 'bool'
'type' => 'boolOrExpr'
},
'totp2fUserCanChangeKey' => {
'default' => 0,
......@@ -3264,7 +3264,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
},
'u2fSelfRegistration' => {
'default' => 0,
'type' => 'bool'
'type' => 'boolOrExpr'
},
'u2fUserCanRemoveKey' => {
'default' => 1,
......@@ -3411,6 +3411,33 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'default' => 'uid',
'type' => 'lmAttrOrMacro'
},
'yubikey2fActivation' => {
'default' => 0,
'type' => 'boolOrExpr'
},
'yubikey2fAuthnLevel' => {
'type' => 'int'
},
'yubikey2fClientID' => {
'type' => 'text'
},
'yubikey2fNonce' => {
'type' => 'text'
},
'yubikey2fPublicIDSize' => {
'default' => 12,
'type' => 'int'
},
'yubikey2fSecretKey' => {
'type' => 'text'
},
'yubikey2fSelfRegistration' => {
'default' => 0,
'type' => 'boolOrExpr'
},
'yubikey2fUrl' => {
'type' => 'text'
},
'yubikeyAuthnLevel' => {
'default' => 3,
'type' => 'int'
......
......@@ -1063,7 +1063,7 @@ sub attributes {
documentation => 'U2F activation',
},
u2fSelfRegistration => {
type => 'bool',
type => 'boolOrExpr',
default => 0,
documentation => 'U2F self registration activation',
},
......@@ -1085,7 +1085,7 @@ sub attributes {
documentation => 'TOTP activation',
},
totp2fSelfRegistration => {
type => 'bool',
type => 'boolOrExpr',
default => 0,
documentation => 'TOTP self registration activation',
},
......@@ -1201,6 +1201,44 @@ sub attributes {
documentation => 'Custom logo for REST 2F',
},
# Yubikey 2FA
yubikey2fActivation => {
type => 'boolOrExpr',
default => 0,
documentation => 'Yubikey second factor activation',
},
yubikey2fSelfRegistration => {
type => 'boolOrExpr',
default => 0,
documentation => 'Yubikey self registration activation',
},
yubikey2fAuthnLevel => {
type => 'int',
documentation =>
'Authentication level for users authentified by Yubikey second factor'
},
yubikey2fClientID => {
type => 'text',
documentation => 'Yubico client ID',
},
yubikey2fSecretKey => {
type => 'text',
documentation => 'Yubico secret key',
},
yubikey2fNonce => {
type => 'text',
documentation => 'Yubico nonce',
},
yubikey2fUrl => {
type => 'text',
documentation => 'Yubico server',
},
yubikey2fPublicIDSize => {
type => 'int',
default => 12,
documentation => 'Yubikey public ID size',
},
# Single session
notifyDeleted => {
default => 1,
......
......@@ -702,6 +702,21 @@ sub tree {
'rest2fLogo',
]
},
{
title => 'yubikey2f',
help => 'yubikey2f.html',
form => 'simpleInputContainer',
nodes => [
'yubikey2fActivation',
'yubikey2fSelfRegistration',
'yubikey2fAuthnLevel',
'yubikey2fClientID',
'yubikey2fSecretKey',
'yubikey2fNonce',
'yubikey2fUrl',
'yubikey2fPublicIDSize',
],
},
]
},
{
......
......@@ -719,7 +719,7 @@
"totp2fInterval":"Interval",
"totp2fIssuer":"TOTP Issuer name",
"totp2fRange":"Range of attempts",
"totp2fSelfRegistration":"Self registration",
"totp2fSelfRegistration":"التسجيل الذاتي",
"totp2fUserCanChangeKey":"Change existing secret",
"trustedDomains":"النطاقات الموثوق بها",
"trustedProxies":"عناوين الآي بي البروكسي الموثوق بها",
......@@ -785,6 +785,15 @@
"whatToTrace":"المستخدم_البعيد",
"whiteList":"القائمة البيضاء",
"XMLcontent":"محتوى XML",
"yubikey2f":"Yubikey",
"yubikey2fActivation":"تفعيل",
"yubikey2fAuthnLevel":"مستوى إثبات الهوية",
"yubikey2fClientID":"API العميل ID",
"yubikey2fNonce":"Nonce",
"yubikey2fPublicIDSize":"حجم الجزء العام لي OTP آي دي",
"yubikey2fSecretKey":"مفتاح سرأل API",
"yubikey2fSelfRegistration":"التسجيل الذاتي",
"yubikey2fUrl":"Service URL",
"yubikeyAuthnLevel":"مستوى إثبات الهوية",
"yubikeyClientID":"API العميل ID",
"yubikeyParams":"معاييرYubikey",
......
......@@ -785,6 +785,15 @@
"whatToTrace":"REMOTE_USER",
"whiteList":"White list",
"XMLcontent":"XML content",
"yubikey2f":"Yubikey",
"yubikey2fActivation":"Activation",
"yubikey2fAuthnLevel":"Authentication level",
"yubikey2fClientID":"API client ID",
"yubikey2fNonce":"Nonce",
"yubikey2fPublicIDSize":"OTP public ID part size",
"yubikey2fSecretKey":"API secret key",
"yubikey2fSelfRegistration":"Self registration",
"yubikey2fUrl":"Service URL",
"yubikeyAuthnLevel":"Authentication level",
"yubikeyClientID":"API client ID",
"yubikeyParams":"Yubikey parameters",
......
......@@ -785,6 +785,15 @@
"whatToTrace":"REMOTE_USER",
"whiteList":"Liste blanche",
"XMLcontent":"Contenu XML",
"yubikey2f":"Yubikey",
"yubikey2fActivation":"Activation",
"yubikey2fAuthnLevel":"Niveau d'authentification",
"yubikey2fClientID":"Identifiant client de l'API",
"yubikey2fNonce":"Nonce",
"yubikey2fPublicIDSize":"Taille de la partie publique de l'OTP",
"yubikey2fSecretKey":"Clef secrète de l'API",
"yubikey2fSelfRegistration":"Auto-enregistrement",
"yubikey2fUrl":"URL du service",
"yubikeyAuthnLevel":"Niveau d'authentification",
"yubikeyClientID":"Identifiant client de l'API",
"yubikeyParams":"Paramètres Yubikey",
......
......@@ -719,7 +719,7 @@
"totp2fInterval":"Interval",
"totp2fIssuer":"TOTP Issuer name",
"totp2fRange":"Range of attempts",
"totp2fSelfRegistration":"Self registration",
"totp2fSelfRegistration":"Auto-registrazione",
"totp2fUserCanChangeKey":"Change existing secret",
"trustedDomains":"Domini attendibili",
"trustedProxies":"IP proxy attendibili",
......@@ -785,6 +785,15 @@
"whatToTrace":"\nREMOTE_USER",
"whiteList":"Lista bianca",
"XMLcontent":"Contenuto XML",
"yubikey2f":"Yubikey",
"yubikey2fActivation":"Attivazione",
"yubikey2fAuthnLevel":"Livello di autenticazione",
"yubikey2fClientID":"ID client API",
"yubikey2fNonce":"Nonce",
"yubikey2fPublicIDSize":"Dimensione della parte ID OTP pubblica",
"yubikey2fSecretKey":"Chiave segreta API",
"yubikey2fSelfRegistration":"Auto-registrazione",
"yubikey2fUrl":"Service URL",
"yubikeyAuthnLevel":"Livello di autenticazione",
"yubikeyClientID":"ID client API",
"yubikeyParams":"Parametri Yubikey",
......
......@@ -719,7 +719,7 @@
"totp2fInterval":"Interval",
"totp2fIssuer":"TOTP Issuer name",
"totp2fRange":"Range of attempts",
"totp2fSelfRegistration":"Self registration",
"totp2fSelfRegistration":"Tự đăng ký",
"totp2fUserCanChangeKey":"Change existing secret",
"trustedDomains":"Miền tin cậy",
"trustedProxies":"proxies IP tin cậy",
......@@ -733,7 +733,7 @@
"u2fActivation":"Kích hoạt",
"u2fAuthnLevel":"Mức xác thực U2F",
"u2fUserCanRemoveKey":"Authorize user to remove U2F key",
"u2fSelfRegistration":"Tự đăng ký ",
"u2fSelfRegistration":"Tự đăng ký",
"u2fSessions":"U2F sessions explorer",
"uid":"Trình định danh",
"unknownAttrOrMacro":"Thuộc tính hoặc macro chưa xác định",
......@@ -785,6 +785,15 @@
"whatToTrace":"REMOTE_USER",
"whiteList":"Danh sách trắng",
"XMLcontent":"Nội dung XML",
"yubikey2f":"Yubikey",
"yubikey2fActivation":"Kích hoạt",
"yubikey2fAuthnLevel":"Mức xác thực",
"yubikey2fClientID":"ID ứng dụng khách API",
"yubikey2fNonce":"Nonce",
"yubikey2fPublicIDSize":"Kích thước phần tử công khai OTP",
"yubikey2fSecretKey":"Khóa bí mật API",
"yubikey2fSelfRegistration":"Tự đăng ký",
"yubikey2fUrl":"Service URL",
"yubikeyAuthnLevel":"Mức xác thực",
"yubikeyClientID":"ID ứng dụng khách API",
"yubikeyParams":"Tham số Yubikey",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -13,10 +13,12 @@ lib/Lemonldap/NG/Portal/2F/Engines/Default.pm
lib/Lemonldap/NG/Portal/2F/Ext2F.pm
lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm
lib/Lemonldap/NG/Portal/2F/Register/U2F.pm
lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm
lib/Lemonldap/NG/Portal/2F/REST.pm
lib/Lemonldap/NG/Portal/2F/TOTP.pm
lib/Lemonldap/NG/Portal/2F/U2F.pm
lib/Lemonldap/NG/Portal/2F/UTOTP.pm
lib/Lemonldap/NG/Portal/2F/Yubikey.pm
lib/Lemonldap/NG/Portal/Auth.pod
lib/Lemonldap/NG/Portal/Auth/_WebForm.pm
lib/Lemonldap/NG/Portal/Auth/AD.pm
......@@ -321,6 +323,7 @@ site/templates/bootstrap/u2fcheck.tpl
site/templates/bootstrap/u2fregister.tpl
site/templates/bootstrap/upgradesession.tpl
site/templates/bootstrap/utotp2fcheck.tpl
site/templates/bootstrap/yubikey2fregister.tpl
site/templates/bootstrap/yubikeyform.tpl
site/templates/common/bullet_go.png
site/templates/common/key.png
......
......@@ -34,8 +34,8 @@ sub init {
my ($self) = @_;
# Set default 2F list
$self->conf->{available2F} ||= 'UTOTP,TOTP,U2F,REST,Ext2F';
$self->conf->{available2FSelfRegistration} ||= 'TOTP,U2F';
$self->conf->{available2F} ||= 'UTOTP,TOTP,U2F,REST,Ext2F,Yubikey';
$self->conf->{available2FSelfRegistration} ||= 'TOTP,U2F,Yubikey';
# Load 2F modules
for my $i ( 0 .. 1 ) {
......
package Lemonldap::NG::Portal::2F::Register::Yubikey;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_FORMEMPTY
PE_ERROR
PE_OK
);
our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Main::Plugin';
# INITIALIZATION
has prefix => ( is => 'rw', default => 'yubikey' );
has template => ( is => 'ro', default => 'yubikey2fregister' );
has logo => ( is => 'rw', default => 'u2f.png' );
sub init {
my ($self) = @_;
$self->conf->{yubikey2fPublicIDSize} ||= 12;
return 1;
}
# RUNNING METHODS
# Main method
sub run {
my ( $self, $req, $action ) = @_;
if ( $action eq 'register' ) {
my $otp = $req->param('otp');
if ( $otp and length($otp) > 12 ) {
my $keys = $req->userData->{_yubikeys} || '';
$keys .= ( $keys ? ', ' : '' )
. substr( $otp, 0, $self->conf->{yubikeyPublicIDSize} );
$self->p->updatePersistentSession( $req, { _yubikeys => $keys } );
}
else {
$self->userLogger->error('Yubikey 2F: no code');
return PE_FORMEMPTY;
}
}
else {
$self->userLogger->error("Unknown Yubikey action $action");
return PE_ERROR;
}
}
1;
package Lemonldap::NG::Portal::2F::Yubikey;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_FORMEMPTY
PE_OK
PE_SENDRESPONSE
);
our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Main::SecondFactor';
# INITIALIZATION
has prefix => ( is => 'ro', default => 'yubikey' );
has logo => ( is => 'rw', default => 'u2f.png' );
has yubi => ( is => 'rw' );
sub init {
my ($self) = @_;
eval { require Auth::Yubikey_WebClient };
if ($@) {
$self->logger->error($@);
return 0;
}
if ( $self->conf->{yubikey2fSelfRegistration}
and $self->conf->{yubikey2fActivation} eq '1' )
{
$self->conf->{yubikey2fActivation} = '$_yubikeys';
}
unless ($self->conf->{yubikey2fClientID}
and $self->conf->{yubikey2fSecretKey} )
{
$self->logger->error(
"Missing mandatory parameters (Client ID and secret key)");
return 0;
}
$self->conf->{yubikey2fPublicIDSize} ||= 12;
$self->yubi(
Auth::Yubikey_WebClient->new(
{
id => $self->conf->{yubikey2fClientID},
api => $self->conf->{yubikey2fSecretKey},
nonce => $self->conf->{yubikey2fNonce},
url => $self->conf->{yubikey2fUrl}
}
)
);
return $self->SUPER::init();
}
sub run {
my ( $self, $req, $token ) = @_;
unless ( $req->{sessionInfo}->{_yubikeys} ) {
$self->userLogger->warn( 'User '
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
. ' has no Yubikey registered' );
return PE_BADCREDENTIALS;
}
# Prepare form
my $tmp = $self->p->sendHtml(
$req,
'ext2fcheck',
params => {
SKIN => $self->conf->{portalSkin},
TOKEN => $token,
TARGET => '/yubikey2fcheck',
INPUTLOGO => 'yubikey.png',
LEGEND => 'clickOnYubikey',
}
);
$self->logger->debug("Display Yubikey form");
$req->response($tmp);
return PE_SENDRESPONSE;
}
sub verify {
my ( $self, $req, $session ) = @_;
my $code;
unless ( $code = $req->param('code') ) {
$self->userLogger->error('Yubikey 2F: no code');
return PE_FORMEMPTY;
}
# Verify OTP
if (
index( substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} ),
$session->{_yubikeys} ) == -1
)
{
$self->userLogger->warn('Yubikey not registered');
return PE_BADCREDENTIALS;
}
if ( $self->yubi->otp($code) ne 'OK' ) {
$self->userLogger->warn('Yubikey verification failed');
return PE_BADCREDENTIALS;
}
PE_OK;
}
1
......@@ -33,6 +33,7 @@ has noRoute => ( is => 'ro' );
sub init {
my ($self) = @_;
unless ( $self->noRoute ) {
$self->logger->debug('Adding '.$self->prefix . '2fcheck routes');
$self->addUnauthRoute(
$self->prefix . '2fcheck' => '_verify',
['POST']
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"اختر أحد التطبيقات المسموح لك بالدخول إليها",
"clickHere":"الرجاء الضغط هنا",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"أغلق جلسة الدخول الموحد (سسو)",
"code": "الشفرة",
"confirmation":"التأكيد",
......@@ -121,8 +122,8 @@
"enterCred":"الرجاء إدخال بيانات الاعتماد الخاصة بك",
"enterExt2fCode":"تم إرسال رمز إليك. الرجاء إدخاله",
"enterOpenIDLogin":"الرجاء إدخال تسجيل الدخول الأوبين إيدي الخاص بك",
"enterYubikey":"يرجى استخدام يوبي كي الخاص بك",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"يرجى استخدام يوبي كي الخاص بك",
"errorMsg":"رسالة خاطئة",
"fillTheForm":"Fill the form",
"firstName":"الاسم الاول",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Close your SSO session",
"code": "Code",
"confirmation":"Confirmation",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Close your SSO session",
"code": "Code",
"confirmation":"Confirmation",
......
......@@ -104,9 +104,10 @@
"changeKey": "Generate new key",
"changePwd":"Change your password",
"checkLastLogins":"Check my last logins",
"chooseApp":"Choose an application your are allowed to access to",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Close your SSO session",
"code": "Code",
"confirmation":"Confirmation",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choisissez votre second facteur",
"chooseApp":"Choisissez une application à laquelle vous êtes autorisé à accéder",
"clickHere":"Cliquez ici",
"clickOnYubikey":"Cliquez sur votre Yubikey",
"closeSSO":"Fermer votre Session SSO",
"code": "Code",
"confirmation":"Confirmation",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"Scegli un'applicazione alla quale ti è consentito l'accesso",
"clickHere":"Per favore clicka qui",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Chiudi la sessione SSO",
"code": "Codice",
"confirmation":"Conferma",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Close your SSO session",
"code": "Code",
"confirmation":"Confirmation",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Close your SSO session",
"code": "Code",
"confirmation":"Confirmation",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Close your SSO session",
"code": "Code",
"confirmation":"Confirmation",
......
......@@ -107,6 +107,7 @@
"choose2f":"Choose your second factor",
"chooseApp":"Chọn một ứng dụng bạn được phép truy cập vào",
"clickHere":"Vui lòng nhấp vào đây",
"clickOnYubikey":"Click on your Yubikey",
"closeSSO":"Đóng phiên SSO của bạn",
"code": "Mã",
"confirmation":"Xác nhận",
......
......@@ -2,7 +2,7 @@
<main id="logincontent" class="container">
<div class="message message-positive alert"><span trspan="enterExt2fCode"></span></div>
<div class="message message-positive alert"><span trspan="<TMPL_IF "LEGEND"><TMPL_VAR "LEGEND"><TMPL_ELSE>enterExt2fCode</TMPL_IF>"></span></div>
<div class="panel panel-default">
......
......@@ -7,22 +7,23 @@
<main id="menucontent" class="container">
<div class="panel panel-info">
<div class="panel-body">
<div id="u2fPermission" trspan="u2fPermission" class="alert alert-info">You may be prompted to allow the site permission to access your security keys. After granting permission, the device will start to blink.</div>
<div class="buttons">
<span id="register" class="btn btn-warning" role="button">
<span class="glyphicon glyphicon-plus-sign"></span>&nbsp;
<span trspan="register">Register</span>
</span>
<span id="verify" class="btn btn-success" role="button">
<span class="glyphicon glyphicon-check"></span>&nbsp;
<span trspan="verify">Verify</span>
</span>
<span id="unregister" class="btn btn-danger" role="button">
<span class="glyphicon glyphicon-minus-sign"></span>&nbsp;
<span trspan="unregister">Unregister</span>
</span>
</div>
</div>
<div id="u2fPermission" trspan="u2fPermission" class="alert alert-info">You may be prompted to allow the site permission to access your security keys. After granting permission, the device will start to blink.
</div>
<div class="buttons">
<span id="register" class="btn btn-warning" role="button">
<span class="glyphicon glyphicon-plus-sign"></span>&nbsp;
<span trspan="register">Register</span>
</span>
<span id="verify" class="btn btn-success" role="button">
<span class="glyphicon glyphicon-check"></span>&nbsp;
<span trspan="verify">Verify</span>
</span>
<span id="unregister" class="btn btn-danger" role="button">
<span class="glyphicon glyphicon-minus-sign"></span>&nbsp;
<span trspan="unregister">Unregister</span>
</span>
</div>
</div>
</div>
</main>
......
<TMPL_INCLUDE NAME="header.tpl">
<div class="container">
<div id="color" class="message message-positive alert"><span id="msg" trspan="clickOnYubikey"></span></div>
</div>
<main id="menucontent" class="container">
<div class="panel panel-info">
<div class="panel-body">
<form action="/2fregisters/yubikey/register" method="post">
<div class="form-group">
<input id="otp" name="otp" />
</div>
</form>
</div>
</div>
</main>
<div class="buttons">
<a id="goback" href="<TMPL_VAR NAME="PORTAL_URL"><TMPL_IF NAME="AUTH_URL">/?url=<TMPL_VAR NAME="AUTH_URL"></TMPL_IF>" class="btn btn-primary" role="button">
<span class="glyphicon glyphicon-home"></span>&nbsp;
<span trspan="goToPortal">Go to portal</span>
</a>
</div>
<TMPL_INCLUDE NAME="footer.tpl">
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment