diff --git a/htdocs/admin/oauthlogintokens.php b/htdocs/admin/oauthlogintokens.php
index b41ae78d4c70db7660373b20b083f0de328c2ff1..93306e8d13cb329f942b3595032b84f650871a69 100644
--- a/htdocs/admin/oauthlogintokens.php
+++ b/htdocs/admin/oauthlogintokens.php
@@ -104,6 +104,11 @@ if ($action == 'setvalue' && $user->admin)
  * View
  */
 
+// Define $urlwithroot
+$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
+$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
+//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
+
 $form = new Form($db);
 
 llxHeader('',$langs->trans("PrintingSetup"));
@@ -127,70 +132,191 @@ if ($mode == 'setup' && $user->admin)
         if (in_array($key[0], array_keys($supportedoauth2array))) $supported=1;
         if (! $supported) continue;     // show only supported
         
+        
+        $OAUTH_SERVICENAME='Unknown';
+        if ($key[0] == 'OAUTH_GITHUB_NAME')
+        {
+            $OAUTH_SERVICENAME='GitHub';
+            $urltorenew=$urlwithroot.'/core/modules/oauth/github_oauthcallback.php?state=user,public_repo&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
+            $urltodelete=$urlwithroot.'/core/modules/oauth/github_oauthcallback.php?action=delete&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
+            $urltocheckperms='https://github.com/settings/applications/';
+        }
+        if ($key[0] == 'OAUTH_GOOGLE_NAME')
+        {
+            $OAUTH_SERVICENAME='Google';
+            $urltorenew=$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?state=userinfo_email,userinfo_profile,cloud_print&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
+            $urltodelete=$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?action=delete&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php');
+            $urltocheckperms='https://security.google.com/settings/security/permissions';
+        }
+        
+        // Show value of token
+        $tokenobj=null;
+        // Token
+        require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
+        require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
+        // Dolibarr storage
+        $storage = new DoliStorage($db, $conf);
+        try
+        {
+            $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
+        }
+        catch(Exception $e)
+        {
+            // Return an error if token not found
+        }
+        
+        // Set other properties
+        $refreshtoken=false;
+        $expiredat='';
+        
+        $expire = false;
+        // Is token expired or will token expire in the next 30 seconds
+        if (is_object($tokenobj)) {
+            $expire = ($tokenobj->getEndOfLife() !== $tokenobj::EOL_NEVER_EXPIRES && $tokenobj->getEndOfLife() !== $tokenobj::EOL_UNKNOWN && time() > ($tokenobj->getEndOfLife() - 30));
+        }
+        
+        // Token expired so we refresh it
+        if (is_object($tokenobj) && $expire) {
+            try {
+                // il faut sauvegarder le refresh token car le provider ne le donne qu'une seule fois
+                $refreshtoken = $tokenobj->getRefreshToken();
+                $tokenobj = $apiService->refreshAccessToken($tokenobj);
+                $tokenobj->setRefreshToken($refreshtoken);
+                $storage->storeAccessToken($OAUTH_SERVICENAME, $tokenobj);
+            } catch (Exception $e) {
+                setEventMessages($e->getMessage(), null, 'errors');
+            }
+        }
+        if ($key[1] != '' && $key[2] != '') {
+            if (is_object($tokenobj)) {
+                $refreshtoken = $tokenobj->getRefreshToken();
+                
+                $endoflife = $tokenobj->getEndOfLife();
+                if ($endoflife == $tokenobj::EOL_NEVER_EXPIRES)
+                {
+                    $expiredat = $langs->trans("Never");
+                }
+                elseif ($endoflife == $tokenobj::EOL_UNKNOWN)
+                {
+                    $expiredat = $langs->trans("Unknown");
+                }
+                else
+                {
+                    $expiredat=dol_print_date($endoflife, "dayhour");
+                }
+            }
+        }
+
+        $submit_enabled=0;
+        
         print '<form method="post" action="'.$_SERVER["PHP_SELF"].'?mode=setup&amp;driver='.$driver.'" autocomplete="off">';
         print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
         print '<input type="hidden" name="action" value="setconst">';
     
         
         print '<table class="noborder" width="100%">'."\n";
-        $var=true;
+        
+        $var=false;
         print '<tr class="liste_titre">';
-        print '<th>'.$langs->trans("Parameters").'</th>';
-        print '<th>'.$langs->trans("Value").'</th>';
-        print '<th>&nbsp;</th>';
+        print '<th class="titlefieldcreate">'.$langs->trans($key[0]).'</th>';
+        print '<th></th>';
+        print '<th></th>';
         print "</tr>\n";
-        $submit_enabled=0;
-    
+        
+        print '<tr '.$bc[$var].'>';
+        print '<td'.($key['required']?' class="required"':'').'>';
+        //var_dump($key);
+        print $langs->trans("OAuthIDSecret").'</td>';
+        print '<td>';
+        print $langs->trans("SeePreviousTab");
+        print '</td>';
+        print '<td>';
+        print '</td>';
+        print '</tr>'."\n";
+        
+        $var = ! $var;
         print '<tr '.$bc[$var].'>';
-        print '<td'.($key['required']?' class=required':'').'>'.$langs->trans($key['varname']).'</td>';
-        print '<td>'.$langs->trans($key['info']).'</td>';
+        print '<td'.($key['required']?' class="required"':'').'>';
+        //var_dump($key);
+        print $langs->trans("IsTokenGenerated");
+        print '</td>';
+        print '<td>';
+        if (is_object($tokenobj)) print $langs->trans("HasAccessToken");
+        else print $langs->trans("NoAccessToken");
+        print '</td>';
         print '<td>';
-        if ($key['varname'] == 'PRINTGCP_TOKEN_ACCESS')
+        // Links to delete/checks token
+        if (is_object($tokenobj))
         {
-            // Delete remote tokens
-            if (! empty($key['delete'])) print '<a class="button" href="'.$key['delete'].'">'.$langs->trans('DeleteAccess').'</a><br><br>';
-            // Request remote token
-            print '<a class="button" href="'.$key['renew'].'">'.$langs->trans('RequestAccess').'</a><br><br>';
-            // Check remote access
-            print $langs->trans("ToCheckDeleteTokenOnProvider", $OAUTH_SERVICENAME_GOOGLE).': <a href="https://security.google.com/settings/security/permissions" target="_google">https://security.google.com/settings/security/permissions</a>';
+            //test on $storage->hasAccessToken($OAUTH_SERVICENAME) ?
+            print '<a class="button" href="'.$urltodelete.'">'.$langs->trans('DeleteAccess').'</a><br><br>';
+        }
+        // Request remote token
+        print '<a class="button" href="'.$urltorenew.'">'.$langs->trans('RequestAccess').'</a><br><br>';
+        // Check remote access
+        if ($urltocheckperms)
+        {
+            print $langs->trans("ToCheckDeleteTokenOnProvider", $OAUTH_SERVICENAME).': <a href="'.$urltocheckperms.'" target="_'.strtolower($OAUTH_SERVICENAME).'">'.$urltocheckperms.'</a>';
         }
         print '</td>';
-        print '</tr>'."\n";
+        print '</tr>';
         
-        // Show value of token
-        if ($key['varname'] == 'PRINTGCP_TOKEN_ACCESS')
+        $var = ! $var;
+        print '<tr '.$bc[$var].'>';
+        print '<td'.($key['required']?' class="required"':'').'>';
+        //var_dump($key);
+        print $langs->trans("Token").'</td>';
+        print '<td colspan="2">';
+        if (is_object($tokenobj))
         {
-            // Token
+            //var_dump($tokenobj);
+            print $tokenobj->getAccessToken().'<br>';
+            //print 'Refresh: '.$tokenobj->getRefreshToken().'<br>';
+            //print 'EndOfLife: '.$tokenobj->getEndOfLife().'<br>';
+            //var_dump($tokenobj->getExtraParams());
+            /*print '<br>Extra: <br><textarea class="quatrevingtpercent">';
+            print ''.join(',',$tokenobj->getExtraParams());
+            print '</textarea>';*/
+        }        
+        print '</td>';
+        print '</tr>'."\n";
+
+        if (is_object($tokenobj))
+        {
+            // Token refresh
+            $var = ! $var;
             print '<tr '.$bc[$var].'>';
-            print '<td>'.$langs->trans("Token").'</td>';
-            print '<td>';
-            // Dolibarr storage
-            $storage = new DoliStorage($db, $conf);
-            try
-            {
-                $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME_GOOGLE);
-            }
-            catch(Exception $e)
-            {
-                // Return an error if token not found
-            }
-            if (is_object($tokenobj))
-            {
-                //var_dump($tokenobj);
-                print $tokenobj->getAccessToken().'<br>';
-                //print 'Refresh: '.$tokenobj->getRefreshToken().'<br>';
-                //print 'EndOfLife: '.$tokenobj->getEndOfLife().'<br>';
-                //var_dump($tokenobj->getExtraParams());
-                /*print '<br>Extra: <br><textarea class="quatrevingtpercent">';
-                print ''.join(',',$tokenobj->getExtraParams());
-                print '</textarea>';*/
-            }
+            print '<td'.($key['required']?' class="required"':'').'>';
+            //var_dump($key);
+            print $langs->trans("TOKEN_REFRESH").'</td>';
+            print '<td colspan="2">';
+            print yn($refreshtoken);
+            print '</td>';
+            print '</tr>';
+    
+            // Token expired
+            $var = ! $var;
+            print '<tr '.$bc[$var].'>';
+            print '<td'.($key['required']?' class="required"':'').'>';
+            //var_dump($key);
+            print $langs->trans("TOKEN_EXPIRED").'</td>';
+            print '<td colspan="2">';
+            print yn($expire);
             print '</td>';
-            print '<td>';
+            print '</tr>';
+            
+            // Token expired at
+            $var = ! $var;
+            print '<tr '.$bc[$var].'>';
+            print '<td'.($key['required']?' class="required"':'').'>';
+            //var_dump($key);
+            print $langs->trans("TOKEN_EXPIRE_AT").'</td>';
+            print '<td colspan="2">';
+            print $expiredat;
             print '</td>';
-            print '</tr>'."\n";
+            print '</tr>';        
         }
-    
+        
         print '</table>';
 
         if (! empty($driver))
@@ -199,7 +325,8 @@ if ($mode == 'setup' && $user->admin)
                 print '<div class="center"><input type="submit" class="button" value="'.dol_escape_htmltag($langs->trans("Modify")).'"></div>';
             }
         }
-    
+
+        
         print '</form>';
     }
     
diff --git a/htdocs/core/lib/oauth.lib.php b/htdocs/core/lib/oauth.lib.php
index 6f47ef6e62b0226478b46f739fc9410d83eae90c..0149b581fa2277c24a3cf0c6936afdc2868dce88 100644
--- a/htdocs/core/lib/oauth.lib.php
+++ b/htdocs/core/lib/oauth.lib.php
@@ -26,9 +26,13 @@
 // Supported OAUTH (a provider is supported when a file xxx_oauthcallback.php is available into htdocs/core/modules/oauth)
 $supportedoauth2array=array(
     'OAUTH_GOOGLE_NAME'=>'google',
-    'OAUTH_GITHUB_NAME'=>'github'
 );
 
+if ($conf->global->MAIN_FEATURES_LEVEL >= 2)
+{
+    $supportedoauth2array['OAUTH_GITHUB_NAME']='github';
+}
+$supportedoauth2array['OAUTH_GITHUB_NAME']='github';
 // API access parameters OAUTH
 $list = array (
     array(
@@ -264,7 +268,7 @@ function oauthadmin_prepare_head()
     $h++;
     
     $head[$h][0] = dol_buildpath('/admin/oauthlogintokens.php', 1);
-    $head[$h][1] = $langs->trans("ManualTokenGeneration");
+    $head[$h][1] = $langs->trans("TokenManager");
     $head[$h][2] = 'tokengeneration';
     $h++;
     
diff --git a/htdocs/core/modules/oauth/github_oauthcallback.php b/htdocs/core/modules/oauth/github_oauthcallback.php
new file mode 100644
index 0000000000000000000000000000000000000000..83c3da66a473e1d6a4e7809b9e4d14273286fc2a
--- /dev/null
+++ b/htdocs/core/modules/oauth/github_oauthcallback.php
@@ -0,0 +1,171 @@
+<?php
+/*
+ * Copyright (C) 2015       Frederic France      <frederic.france@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ *      \file       htdocs/core/modules/oauth/github_oauthcallback.php
+ *      \ingroup    oauth
+ *      \brief      Page to get oauth callback
+ */
+
+require '../../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
+use OAuth\Common\Storage\DoliStorage;
+use OAuth\Common\Consumer\Credentials;
+use OAuth\OAuth2\Service\GitHub;
+
+// Define $urlwithroot
+$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
+$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
+//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
+
+
+
+$action = GETPOST('action', 'alpha');
+$backtourl = GETPOST('backtourl', 'alpha');
+
+
+/**
+ * Create a new instance of the URI class with the current URI, stripping the query string
+ */
+$uriFactory = new \OAuth\Common\Http\Uri\UriFactory();
+//$currentUri = $uriFactory->createFromSuperGlobalArray($_SERVER);
+//$currentUri->setQuery('');
+$currentUri = $uriFactory->createFromAbsolute($urlwithroot.'/core/modules/oauth/github_oauthcallback.php');
+
+
+/**
+ * Load the credential for the service
+ */
+
+/** @var $serviceFactory \OAuth\ServiceFactory An OAuth service factory. */
+$serviceFactory = new \OAuth\ServiceFactory();
+$httpClient = new \OAuth\Common\Http\Client\CurlClient();
+// TODO Set options for proxy and timeout
+// $params=array('CURLXXX'=>value, ...)
+//$httpClient->setCurlParameters($params);
+$serviceFactory->setHttpClient($httpClient);
+
+// Dolibarr storage
+$storage = new DoliStorage($db, $conf);
+
+// Setup the credentials for the requests
+$credentials = new Credentials(
+    $conf->global->OAUTH_GITHUB_ID,
+    $conf->global->OAUTH_GITHUB_SECRET,
+    $currentUri->getAbsoluteUri()
+);
+
+$requestedpermissionsarray=array();
+if (GETPOST('state')) $requestedpermissionsarray=explode(',', GETPOST('state'));       // Example: 'userinfo_email,userinfo_profile,cloud_print'. 'state' parameter is standard to retrieve some parameters back
+if ($action != 'delete' && empty($requestedpermissionsarray))
+{
+    print 'Error, parameter state is not defined';
+    exit;
+}
+//var_dump($requestedpermissionsarray);exit;
+
+// Instantiate the Api service using the credentials, http client and storage mechanism for the token
+/** @var $apiService Service */
+$apiService = $serviceFactory->createService('GitHub', $credentials, $storage, $requestedpermissionsarray);
+
+// access type needed to have oauth provider refreshing token
+//$apiService->setAccessType('offline');
+
+$langs->load("oauth");
+
+
+/*
+ * Actions
+ */
+
+
+if ($action == 'delete') 
+{
+    $storage->clearToken('GitHub');
+    
+    setEventMessages($langs->trans('TokenDeleted'), null, 'mesgs');
+    
+    header('Location: ' . $backtourl);
+    exit();
+} 
+
+if (! empty($_GET['code']))     // We are coming from oauth provider page
+{
+    //llxHeader('',$langs->trans("OAuthSetup"));
+
+    //$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackToModuleList").'</a>';
+    //print load_fiche_titre($langs->trans("OAuthSetup"),$linkback,'title_setup');
+
+    //dol_fiche_head();
+    // retrieve the CSRF state parameter
+    $state = isset($_GET['state']) ? $_GET['state'] : null;
+    //print '<table>';
+
+    // This was a callback request from service, get the token
+    try {
+        //var_dump($_GET['code']);
+        //var_dump($state);
+        //var_dump($apiService);      // OAuth\OAuth2\Service\GitHub
+        
+        //$token = $apiService->requestAccessToken($_GET['code'], $state);
+        $token = $apiService->requestAccessToken($_GET['code']);                
+        // Github is a service that does not need state yo be stored.
+        // Into constructor of GitHub, the call
+        // parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri)
+        // has not the ending parameter to true like the Google class constructor.
+
+        setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs');   // Stored into object managed by class DoliStorage so into table oauth_token
+    } catch (Exception $e) {
+        print $e->getMessage();
+    }
+
+    $backtourl = $_SESSION["backtourlsavedbeforeoauthjump"];
+    unset($_SESSION["backtourlsavedbeforeoauthjump"]);
+    
+    header('Location: ' . $backtourl);
+    exit();
+}
+else // If entry on page with no parameter, we arrive here
+{
+    $_SESSION["backtourlsavedbeforeoauthjump"]=$backtourl;
+    
+    // This may create record into oauth_state before the header redirect.
+    // Creation of record with state in this tables depend on the Provider used (see its constructor).
+    if (GETPOST('state'))
+    {
+        $url = $apiService->getAuthorizationUri(array('state'=>GETPOST('state')));
+    }
+    else
+    {
+        $url = $apiService->getAuthorizationUri();      // Parameter state will be randomly generated
+    }
+    
+    // we go on oauth provider authorization page
+    header('Location: ' . $url);
+    exit();
+}
+
+
+/*
+ * View
+ */
+
+// No view at all, just actions
+
+$db->close();
+
diff --git a/htdocs/core/modules/oauth/google_oauthcallback.php b/htdocs/core/modules/oauth/google_oauthcallback.php
index 3068a29098c6343bd9dfba1961827cc368512bb1..c69493ed9a554dc5988a53361387c0797846d741 100644
--- a/htdocs/core/modules/oauth/google_oauthcallback.php
+++ b/htdocs/core/modules/oauth/google_oauthcallback.php
@@ -17,7 +17,7 @@
  */
 
 /**
- *      \file       htdocs/core/modules/oauth/getoauthcallback.php
+ *      \file       htdocs/core/modules/oauth/google_oauthcallback.php
  *      \ingroup    oauth
  *      \brief      Page to get oauth callback
  */
@@ -83,7 +83,7 @@ if ($action != 'delete' && empty($requestedpermissionsarray))
 /** @var $apiService Service */
 $apiService = $serviceFactory->createService('Google', $credentials, $storage, $requestedpermissionsarray);
 
-// access type needed for google refresh token
+// access type needed to have oauth provider refreshing token
 $apiService->setAccessType('offline');
 
 $langs->load("oauth");
@@ -104,7 +104,7 @@ if ($action == 'delete')
     exit();
 } 
 
-if (! empty($_GET['code']))     // We are coming from Google oauth page
+if (! empty($_GET['code']))     // We are coming from oauth provider page
 {
     //llxHeader('',$langs->trans("OAuthSetup"));
 
@@ -121,6 +121,7 @@ if (! empty($_GET['code']))     // We are coming from Google oauth page
         //var_dump($_GET['code']);
         //var_dump($state);
         //var_dump($apiService);      // OAuth\OAuth2\Service\Google
+        
         $token = $apiService->requestAccessToken($_GET['code'], $state);
         
         setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs');   // Stored into object managed by class DoliStorage so into table oauth_token
@@ -138,6 +139,8 @@ else // If entry on page with no parameter, we arrive here
 {
     $_SESSION["backtourlsavedbeforeoauthjump"]=$backtourl;
     
+    // This may create record into oauth_state before the header redirect.
+    // Creation of record with state in this tables depend on the Provider used (see its constructor).
     if (GETPOST('state'))
     {
         $url = $apiService->getAuthorizationUri(array('state'=>GETPOST('state')));
@@ -146,7 +149,8 @@ else // If entry on page with no parameter, we arrive here
     {
         $url = $apiService->getAuthorizationUri();      // Parameter state will be randomly generated
     }
-    // we go on google authorization page
+    
+    // we go on oauth provider authorization page
     header('Location: ' . $url);
     exit();
 }
diff --git a/htdocs/core/modules/printing/printgcp.modules.php b/htdocs/core/modules/printing/printgcp.modules.php
index 3d086466c872d746d7733fe15fcc9886d3fecb10..baa5106ae93b81f7c2791d88fce7391fd55b6bc0 100644
--- a/htdocs/core/modules/printing/printgcp.modules.php
+++ b/htdocs/core/modules/printing/printgcp.modules.php
@@ -66,6 +66,7 @@ class printing_printgcp extends PrintingDriver
         $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
         $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
         //$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
+
         $this->db = $db;
         
         if (!$conf->oauth->enabled) {
@@ -117,10 +118,28 @@ class printing_printgcp extends PrintingDriver
                 $this->conf[] = array('varname'=>'PRINTGCP_INFO', 'info'=>'GoogleAuthConfigured', 'type'=>'info');
                 $this->conf[] = array('varname'=>'PRINTGCP_TOKEN_ACCESS', 'info'=>$access, 'type'=>'info', 'renew'=>$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?state=userinfo_email,userinfo_profile,cloud_print&backtourl='.urlencode(DOL_URL_ROOT.'/printing/admin/printing.php?mode=setup&driver=printgcp'), 'delete'=>($storage->hasAccessToken($this->OAUTH_SERVICENAME_GOOGLE)?$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?action=delete&backtourl='.urlencode(DOL_URL_ROOT.'/printing/admin/printing.php?mode=setup&driver=printgcp'):''));
                 if ($token_ok) {
+                    $expiredat='';
+                    
                     $refreshtoken = $token->getRefreshToken();
+                    
+                    $endoflife=$token->getEndOfLife();
+
+                    if ($endoflife == $token::EOL_NEVER_EXPIRES)
+                    {
+                        $expiredat = $langs->trans("Never");
+                    }
+                    elseif ($endoflife == $token::EOL_UNKNOWN)
+                    {
+                        $expiredat = $langs->trans("Unknown");
+                    }
+                    else
+                    {
+                        $expiredat=dol_print_date($endoflife, "dayhour");
+                    }
+                    
                     $this->conf[] = array('varname'=>'TOKEN_REFRESH',   'info'=>((! empty($refreshtoken))?'Yes':'No'), 'type'=>'info');
                     $this->conf[] = array('varname'=>'TOKEN_EXPIRED',   'info'=>($expire?'Yes':'No'), 'type'=>'info');
-                    $this->conf[] = array('varname'=>'TOKEN_EXPIRE_AT', 'info'=>(dol_print_date($token->getEndOfLife(), "dayhour")), 'type'=>'info');
+                    $this->conf[] = array('varname'=>'TOKEN_EXPIRE_AT', 'info'=>($expiredat), 'type'=>'info');
                 }
                 /*
                 if ($storage->hasAccessToken($this->OAUTH_SERVICENAME_GOOGLE)) {
diff --git a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
index 7e82bb63cb28e071e0a4e67daac0db9568d20c81..463afe194dba5aea890b3544a94b3b9c03e70b37 100644
--- a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
+++ b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
@@ -97,7 +97,7 @@ class DoliStorage implements TokenStorageInterface
             $this->tokens = array();
         }
         $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."oauth_token";
-        $sql.= " WHERE service='".$service."' AND entity=1";
+        $sql.= " WHERE service='".$this->db->escape($service)."' AND entity=1";
         $resql = $this->db->query($sql);
         if (! $resql)
         {
@@ -113,7 +113,7 @@ class DoliStorage implements TokenStorageInterface
         } else {
             // save
             $sql = "INSERT INTO ".MAIN_DB_PREFIX."oauth_token (service, token, entity)";
-            $sql.= " VALUES ('".$service."', '".$this->db->escape($serializedToken)."', 1)";
+            $sql.= " VALUES ('".$this->db->escape($service)."', '".$this->db->escape($serializedToken)."', 1)";
             $resql = $this->db->query($sql);
         }
         //print $sql;
@@ -130,7 +130,7 @@ class DoliStorage implements TokenStorageInterface
         // get from db
         dol_syslog("hasAccessToken service=".$service);
         $sql = "SELECT token FROM ".MAIN_DB_PREFIX."oauth_token";
-        $sql.= " WHERE service='".$service."'";
+        $sql.= " WHERE service='".$this->db->escape($service)."'";
         $resql = $this->db->query($sql);
         if (! $resql)
         {
@@ -159,7 +159,7 @@ class DoliStorage implements TokenStorageInterface
         //    unset($tokens[$service]);
 
             $sql = "DELETE FROM ".MAIN_DB_PREFIX."oauth_token";
-            $sql.= " WHERE service='".$service."'";
+            $sql.= " WHERE service='".$this->db->escape($service)."'";
             $resql = $this->db->query($sql);
         //}
 
@@ -189,7 +189,7 @@ class DoliStorage implements TokenStorageInterface
 
         }
 
-        throw new AuthorizationStateNotFoundException('State not found in conf, are you sure you stored it?');
+        throw new AuthorizationStateNotFoundException('State not found in db, are you sure you stored it?');
     }
 
     /**
@@ -207,7 +207,7 @@ class DoliStorage implements TokenStorageInterface
         $this->states[$service] = $state;
 
         $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."oauth_state";
-        $sql.= " WHERE service='".$service."' AND entity=1";
+        $sql.= " WHERE service='".$this->db->escape($service)."' AND entity=1";
         $resql = $this->db->query($sql);
         if (! $resql)
         {
@@ -223,7 +223,7 @@ class DoliStorage implements TokenStorageInterface
         } else {
             // save
             $sql = "INSERT INTO ".MAIN_DB_PREFIX."oauth_state (service, state, entity)";
-            $sql.= " VALUES ('".$service."', '".$state."', 1)";
+            $sql.= " VALUES ('".$this->db->escape($service)."', '".$this->db->escape($state)."', 1)";
             $resql = $this->db->query($sql);
         }
 
@@ -236,9 +236,10 @@ class DoliStorage implements TokenStorageInterface
      */
     public function hasAuthorizationState($service)
     {
-        // get from db
+        // get state from db
+        dol_syslog("get state from db");
         $sql = "SELECT state FROM ".MAIN_DB_PREFIX."oauth_state";
-        $sql.= " WHERE service='".$service."'";
+        $sql.= " WHERE service='".$this->db->escape($service)."'";
         $resql = $this->db->query($sql);
         $result = $this->db->fetch_array($resql);
         $states[$service] = $result[state];
diff --git a/htdocs/langs/en_US/oauth.lang b/htdocs/langs/en_US/oauth.lang
index f4df2dc3dda082c3eb55a1cea692275cd931329d..cafca379f6f1dc58b19c8d9a33f2e2405572cf9c 100644
--- a/htdocs/langs/en_US/oauth.lang
+++ b/htdocs/langs/en_US/oauth.lang
@@ -2,15 +2,20 @@
 ConfigOAuth=Oauth Configuration
 OAuthServices=OAuth services
 ManualTokenGeneration=Manual token generation
+TokenManager=Token manager
+IsTokenGenerated=Is token generated ?
 NoAccessToken=No access token saved into local database
 HasAccessToken=A token was generated and saved into local database
-NewTokenStored=Token received ans saved
-ToCheckDeleteTokenOnProvider=To check/delete authorization saved by %s OAuth provider
+NewTokenStored=Token received and saved
+ToCheckDeleteTokenOnProvider=Click here to check/delete authorization saved by %s OAuth provider
 TokenDeleted=Token deleted
 RequestAccess=Click here to request/renew access and receive a new token to save
 DeleteAccess=Click here to delete token
 UseTheFollowingUrlAsRedirectURI=Use the following URL as the Redirect URI when creating your credential on your OAuth provider:
 ListOfSupportedOauthProviders=Enter here credential provided by your OAuth2 provider. Only supported OAuth2 providers are visible here. This setup may be used by other modules that need OAuth2 authentication.
+OAuthSetupForLogin=Page to generate an OAuth token
+SeePreviousTab=See previous tab
+OAuthIDSecret=OAuth ID and Secret
 TOKEN_REFRESH=Token Refresh Present
 TOKEN_EXPIRED=Token expired
 TOKEN_EXPIRE_AT=Token expire at
diff --git a/htdocs/langs/en_US/printing.lang b/htdocs/langs/en_US/printing.lang
index d6cf49bd525f4f256a636f9cb22c8358f04ff465..a357409289ad27fc6e81728dfeef9d8efd148dbd 100644
--- a/htdocs/langs/en_US/printing.lang
+++ b/htdocs/langs/en_US/printing.lang
@@ -46,6 +46,6 @@ IPP_Media=Printer media
 IPP_Supported=Type of media
 DirectPrintingJobsDesc=This page lists printing jobs found for available printers.
 GoogleAuthNotConfigured=Google OAuth setup not done. Enable module OAuth and set a Google ID/Secret.
-GoogleAuthConfigured=Google OAuth credentials found into setup of module OAuth.
+GoogleAuthConfigured=Google OAuth credentials were found into setup of module OAuth.
 PrintingDriverDescprintgcp=Configuration variables for printing driver Google Cloud Print.
 PrintTestDescprintgcp=List of Printers for Google Cloud Print.
diff --git a/htdocs/printing/admin/printing.php b/htdocs/printing/admin/printing.php
index 8e9a9d20b9a811eab87202a62d88e893fa129654..8d9609459ed2323200f2daff61692a89d9cfbee0 100644
--- a/htdocs/printing/admin/printing.php
+++ b/htdocs/printing/admin/printing.php
@@ -161,9 +161,19 @@ if ($mode == 'setup' && $user->admin)
                     break;
                 case "info":    // Google Api setup or Google OAuth Token
                     print '<tr '.$bc[$var].'>';
-                    print '<td'.($key['required']?' class=required':'').'>'.$langs->trans($key['varname']).'</td>';
+                    print '<td'.($key['required']?' class=required':'').'>';
+                    if ($key['varname'] == 'PRINTGCP_TOKEN_ACCESS')
+                    {
+                        print $langs->trans("IsTokenGenerated");
+                    }
+                    else
+                    {
+                        print $langs->trans($key['varname']);
+                    }
+                    print '</td>';
                     print '<td>'.$langs->trans($key['info']).'</td>';
                     print '<td>';
+                    //var_dump($key);
                     if ($key['varname'] == 'PRINTGCP_TOKEN_ACCESS')
                     {
                         // Delete remote tokens
@@ -187,7 +197,8 @@ if ($mode == 'setup' && $user->admin)
                 // Token
                 print '<tr '.$bc[$var].'>';
                 print '<td>'.$langs->trans("Token").'</td>';
-                print '<td>';
+                print '<td colspan="2">';
+                $tokenobj=null;
                 // Dolibarr storage
                 $storage = new DoliStorage($db, $conf);
                 try
@@ -210,8 +221,6 @@ if ($mode == 'setup' && $user->admin)
                     print '</textarea>';*/
                 }
                 print '</td>';
-                print '<td>';
-                print '</td>';
                 print '</tr>'."\n";
             }
         }