1
|
|
|
<?php
|
2
|
|
|
declare(strict_types=1);
|
3
|
|
|
|
4
|
|
|
namespace SKien\Google;
|
5
|
|
|
|
6
|
|
|
/**
|
7
|
|
|
* Helper class for the maintenance of the oauth client data and tokens.
|
8
|
|
|
*
|
9
|
|
|
* > **Note:** <br/>
|
10
|
|
|
* > This class ofers the functionality to save tokens in files on the server.
|
11
|
|
|
* > This 'mode' was implemented for development purposes:
|
12
|
|
|
* > - for easier read the tokens returned by the google API
|
13
|
|
|
* > - to work on a local development environment where the development server
|
14
|
|
|
* > is NOT the localhost but the redirectURI also is not accessible from
|
15
|
|
|
* > outside. In that case, only the login have to be made directly on the
|
16
|
|
|
* > machine where the webserver runs - all subsequent calls to the API uses
|
17
|
|
|
* > the tokens from the files created after the oauth-login has called the
|
18
|
|
|
* > redirectURI.
|
19
|
|
|
* >
|
20
|
|
|
* > **For security issues DO NOT USE THIS MODE for public environments!!!**
|
21
|
|
|
*
|
22
|
|
|
* @author Stefanius <[email protected]>
|
23
|
|
|
* @copyright MIT License - see the LICENSE file for details
|
24
|
|
|
*/
|
25
|
|
|
class GSecrets
|
26
|
|
|
{
|
27
|
|
|
/** refreshtoken are saved in cookies */
|
28
|
|
|
public const TOKEN_COOKIE = 0;
|
29
|
|
|
/** refreshtoken are saved in files on the server */
|
30
|
|
|
public const TOKEN_FILE = 1;
|
31
|
|
|
|
32
|
|
|
/** Filename to save access token */
|
33
|
|
|
protected const ACCESS_TOKEN = 'google_access_token';
|
34
|
|
|
/** Filename to save refresh token */
|
35
|
|
|
protected const REFRESH_TOKEN = 'google_refresh_token';
|
36
|
|
|
|
37
|
|
|
/** @var int where to save the access- and refreshtoken */
|
38
|
|
|
protected int $iSaveTokensAt = self::TOKEN_COOKIE;
|
39
|
|
|
/** @var int time, how long the login is valid (-> how long is the refreshToken saved) */
|
40
|
|
|
protected int $iKeepLoggedIn = 0;
|
41
|
|
|
/** @var string the path, where the secrets can be found */
|
42
|
|
|
protected string $strSecretsPath = './secrets/';
|
43
|
|
|
/** @var string the path, where the secrets can be found */
|
44
|
|
|
protected string $strSecretsFilename = 'google_secrets.json';
|
45
|
|
|
|
46
|
|
|
/**
|
47
|
|
|
* Create an instance of the class.
|
48
|
|
|
* **DO NOT USE `$iSaveTokensAt = self::TOKEN_FILE` IN PUBLIC ENVIRONMENT** <br/>
|
49
|
|
|
* In 'self::TOKEN_FILE - Mode' the `$iKeepLoggedIn` param is ignored since the tokens
|
50
|
|
|
* saved in files that never expires unless they are deleted manually...
|
51
|
|
|
* @param int $iKeepLoggedIn 0 (default) -> session only, -1 -> 'forever', other value -> days to keep the login
|
52
|
|
|
* @param int $iSaveTokensAt `self::TOKEN_COOKIE` (default) or `self::TOKEN_FILE`
|
53
|
|
|
*/
|
54
|
|
|
public function __construct(int $iKeepLoggedIn = 0, int $iSaveTokensAt = self::TOKEN_COOKIE)
|
55
|
|
|
{
|
56
|
|
|
$this->iSaveTokensAt = $iSaveTokensAt;
|
57
|
|
|
$this->iKeepLoggedIn = ($iKeepLoggedIn * 86400);
|
58
|
|
|
if ($iKeepLoggedIn < 0) {
|
59
|
|
|
// -1 means nearly 'forever' ... with the following value login expires in about 20 years
|
60
|
|
|
$this->iKeepLoggedIn = 630720000;
|
61
|
|
|
}
|
62
|
|
|
}
|
63
|
|
|
|
64
|
|
|
/**
|
65
|
|
|
* Set the path where the secrets- and token files are located.
|
66
|
|
|
* The directory must not be write-protected and should be protected
|
67
|
|
|
* agains access from outside (.htacces: `deny from all`).
|
68
|
|
|
* @param string $strSecretsPath
|
69
|
|
|
*/
|
70
|
|
|
public function setSecretsPath(string $strSecretsPath) : void
|
71
|
|
|
{
|
72
|
|
|
$this->strSecretsPath = rtrim($strSecretsPath, '/') . '/';
|
73
|
|
|
}
|
74
|
|
|
|
75
|
|
|
/**
|
76
|
|
|
* Sets the filename of the google oauth client configuration file.
|
77
|
|
|
* @param string $strSecretsFilename
|
78
|
|
|
*/
|
79
|
|
|
public function setSecretsFilename(string $strSecretsFilename) : void
|
80
|
|
|
{
|
81
|
|
|
$this->strSecretsFilename = $strSecretsFilename;
|
82
|
|
|
}
|
83
|
|
|
|
84
|
|
|
/**
|
85
|
|
|
* Get full path and filename of the oauth client configuration file.
|
86
|
|
|
* @return string
|
87
|
|
|
*/
|
88
|
|
|
public function getClientSecrets() : string
|
89
|
|
|
{
|
90
|
|
|
return $this->strSecretsPath . $this->strSecretsFilename;
|
91
|
|
|
}
|
92
|
|
|
|
93
|
|
|
/**
|
94
|
|
|
* Gets the last saved accesToken.
|
95
|
|
|
* @return array<mixed>
|
96
|
|
|
*/
|
97
|
|
|
public function getAccessToken() : array
|
98
|
|
|
{
|
99
|
|
|
$aToken = [];
|
100
|
|
|
if (isset($_COOKIE[self::ACCESS_TOKEN])) {
|
101
|
|
|
$aToken = json_decode($_COOKIE[self::ACCESS_TOKEN], true);
|
102
|
|
|
if ($aToken === false) {
|
103
|
|
|
$aToken = [];
|
104
|
|
|
}
|
105
|
|
|
}
|
106
|
|
|
return $aToken;
|
107
|
|
|
}
|
108
|
|
|
|
109
|
|
|
/**
|
110
|
|
|
* Save accessToken.
|
111
|
|
|
* @param array<mixed> $aToken
|
112
|
|
|
*/
|
113
|
|
|
public function saveAccessToken(array $aToken) : void
|
114
|
|
|
{
|
115
|
|
|
$strToken = json_encode($aToken);
|
116
|
|
|
if ($strToken !== false) {
|
117
|
|
|
// the access token has a limited validity anyway, we set the lifetime of this
|
118
|
|
|
// cookie to the session
|
119
|
|
|
$this->setCookie(self::ACCESS_TOKEN, $strToken);
|
120
|
|
|
if ($this->iSaveTokensAt == self::TOKEN_FILE) {
|
121
|
|
|
// since self::TOKEN_FILE only should be used in debug/local environment
|
122
|
|
|
// we just save the accesToken to file to get easier access to its value...
|
123
|
|
|
file_put_contents($this->strSecretsPath . self::ACCESS_TOKEN . '.json', $strToken);
|
124
|
|
|
}
|
125
|
|
|
}
|
126
|
|
|
}
|
127
|
|
|
|
128
|
|
|
/**
|
129
|
|
|
* Gets the last saved refreshToken.
|
130
|
|
|
* @return string
|
131
|
|
|
*/
|
132
|
|
|
public function getRefreshToken() : string
|
133
|
|
|
{
|
134
|
|
|
$strRefreshToken = '';
|
135
|
|
|
if ($this->iSaveTokensAt == self::TOKEN_COOKIE) {
|
136
|
|
|
$strRefreshToken = $_COOKIE[self::REFRESH_TOKEN] ?? '';
|
137
|
|
|
} else if (file_exists($this->strSecretsPath . self::REFRESH_TOKEN . '.txt')) {
|
138
|
|
|
$strRefreshToken = file_get_contents($this->strSecretsPath . self::REFRESH_TOKEN . '.txt');
|
139
|
|
|
}
|
140
|
|
|
return $strRefreshToken;
|
141
|
|
|
}
|
142
|
|
|
|
143
|
|
|
/**
|
144
|
|
|
* Save the refreshToken.
|
145
|
|
|
* @param string $strRefreshToken
|
146
|
|
|
*/
|
147
|
|
|
public function saveRefreshToken(string $strRefreshToken) : void
|
148
|
|
|
{
|
149
|
|
|
if ($this->iSaveTokensAt == self::TOKEN_COOKIE) {
|
150
|
|
|
$iExpires = ($this->iKeepLoggedIn > 0) ? time() + $this->iKeepLoggedIn : 0;
|
151
|
|
|
$this->setCookie(self::REFRESH_TOKEN, $strRefreshToken, $iExpires);
|
152
|
|
|
} else {
|
153
|
|
|
file_put_contents($this->strSecretsPath . self::REFRESH_TOKEN . '.txt', $strRefreshToken);
|
154
|
|
|
}
|
155
|
|
|
}
|
156
|
|
|
|
157
|
|
|
/**
|
158
|
|
|
* Logout from the google account.
|
159
|
|
|
* Deletes a saved access- and refreshToken.
|
160
|
|
|
*/
|
161
|
|
|
public function logout() : void
|
162
|
|
|
{
|
163
|
|
|
$this->setCookie(self::ACCESS_TOKEN, '');
|
164
|
|
|
if ($this->iSaveTokensAt == self::TOKEN_COOKIE) {
|
165
|
|
|
$this->setCookie(self::REFRESH_TOKEN, '');
|
166
|
|
|
} else {
|
167
|
|
|
/** @scrutinizer ignore-unhandled */
|
168
|
|
|
@unlink($this->strSecretsPath . self::REFRESH_TOKEN . '.txt');
|
169
|
|
|
/** @scrutinizer ignore-unhandled */
|
170
|
|
|
@unlink($this->strSecretsPath . self::ACCESS_TOKEN . '.json');
|
171
|
|
|
}
|
172
|
|
|
}
|
173
|
|
|
|
174
|
|
|
/**
|
175
|
|
|
* Sets the specified cookie.
|
176
|
|
|
* @param string $strCookie
|
177
|
|
|
* @param string $strValue
|
178
|
|
|
* @param int $iExpires
|
179
|
|
|
*/
|
180
|
|
|
private function setCookie(string $strCookie, string $strValue, int $iExpires = 0) : void
|
181
|
|
|
{
|
182
|
|
|
setcookie($strCookie, $strValue, [
|
183
|
|
|
'expires' => $iExpires,
|
184
|
|
|
'path' => '/',
|
185
|
|
|
'domain' => '',
|
186
|
|
|
'secure' => false,
|
187
|
|
|
'httponly' => false,
|
188
|
|
|
'samesite' => 'Lax'
|
189
|
|
|
]);
|
190
|
|
|
}
|
191
|
|
|
} |