ClientSideSession.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2020 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
11 namespace wcmf\lib\core\impl;
12 
13 use Lcobucci\JWT\Builder;
14 use Lcobucci\JWT\Parser;
15 use Lcobucci\JWT\Signer\Hmac\Sha256;
16 use Lcobucci\JWT\ValidationData;
24 
25 /**
26  * ClientSideSession has no server state as it stores the data in cookies.
27  *
28  * @author ingo herwig <ingo@wemove.com>
29  */
31 
32  const TOKEN_HEADER = 'Authorization';
33  const AUTH_TYPE = 'Bearer';
34  const AUTH_USER_NAME = 'auth_user';
35 
36  private $cookiePrefix = '';
37  private $tokenName = '';
38  private $token = null;
39 
40  private $key = null;
41 
42  /**
43  * Constructor
44  * @param $configuration
45  */
46  public function __construct(Configuration $configuration) {
47  $this->cookiePrefix = strtolower(StringUtil::slug($configuration->getValue('title', 'application')));
48  $this->tokenName = $this->getCookiePrefix().'-auth-token';
49  $this->key = $configuration->getValue('secret', 'application');
50  }
51 
52  /**
53  * @see TokenBasedSession::getHeaderName()
54  */
55  public function getHeaderName() {
56  return self::TOKEN_HEADER;
57  }
58 
59  /**
60  * @see TokenBasedSession::getCookieName()
61  */
62  public function getCookieName() {
63  return $this->tokenName;
64  }
65 
66  /**
67  * @see Session::isStarted()
68  */
69  public function isStarted() {
70  return sizeof(array_filter(array_keys($_COOKIE), function($key) {
71  return strpos($key, $this->getCookiePrefix()) === 0;
72  })) > 0;
73  }
74 
75  /**
76  * @see Session::getID()
77  */
78  public function getID() {
79  return null;
80  }
81 
82  /**
83  * @see Session::get()
84  */
85  public function get($key, $default=null) {
86  $value = $default;
87  if ($key === self::AUTH_USER_NAME) {
88  // auth user is stored in the token cookie
89  $value = $this->getAuthUser();
90  }
91  elseif (isset($_COOKIE[$key])) {
92  $value = $this->unserializeValue($_COOKIE[$key]);
93  }
94  return $value;
95  }
96 
97  /**
98  * @see Session::set()
99  */
100  public function set($key, $value) {
101  // don't encode auth token value
102  $encodedValue = ($key !== $this->getCookieName()) ? $this->serializeValue($value) : $value;
103  if (!headers_sent()) {
104  setcookie($key, $encodedValue, 0, '/', '', URIUtil::isHttps(), true);
105  }
106  $_COOKIE[$key] = $encodedValue;
107  }
108 
109  /**
110  * @see Session::remove()
111  */
112  public function remove($key) {
113  if (!headers_sent()) {
114  setcookie($key, false, 0, '/', '', URIUtil::isHttps(), true);
115  }
116  unset($_COOKIE[$key]);
117  }
118 
119  /**
120  * @see Session::exist()
121  */
122  public function exist($key) {
123  $result = isset($_COOKIE[$key]);
124  return $result;
125  }
126 
127  /**
128  * @see Session::clear()
129  */
130  public function clear() {
131  foreach(array_keys($_COOKIE) as $key) {
132  $this->remove($key);
133  }
134  }
135 
136  /**
137  * @see Session::destroy()
138  */
139  public function destroy() {
140  // TODO invalidate jwt
141  $this->clear();
142  }
143 
144  /**
145  * @see Session::setAuthUser()
146  */
147  public function setAuthUser($login) {
148  $this->token = $this->createToken($login);
149  $this->set($this->tokenName, $this->token);
150  }
151 
152  /**
153  * @see Session::getAuthUser()
154  */
155  public function getAuthUser() {
157  // check for auth user in token
158  if (($data = $this->getTokenData()) !== null && isset($data[self::AUTH_USER_NAME])) {
159  $login = $data[self::AUTH_USER_NAME]->getValue();
160  }
161  return $login;
162  }
163 
164  /**
165  * Get the cookie prefix
166  * @return String
167  */
168  protected function getCookiePrefix() {
169  return $this->cookiePrefix;
170  }
171 
172  /**
173  * Create the token for the given login
174  * @param $login
175  * @return String
176  */
177  protected function createToken($login) {
178  $jwt = (new Builder())
179  ->issueBy($this->getTokenIssuer())
180  ->issuedAt(time())
181  ->expiresAt(time()+3600)
182  ->withClaim(self::AUTH_USER_NAME, $login)
183  ->getToken($this->getTokenSigner(), $this->key);
184  return $jwt->__toString();
185  }
186 
187  /**
188  * Get the token issuer
189  * @return String
190  */
191  protected function getTokenIssuer() {
192  return URIUtil::getProtocolStr().$_SERVER['HTTP_HOST'];
193  }
194 
195  /**
196  * Get the token issuer
197  * @return String
198  */
199  protected function getTokenSigner() {
200  return new Sha256();
201  }
202 
203  /**
204  * Get the claims stored in the JWT
205  * @return Associative array
206  */
207  protected function getTokenData() {
208  $result = null;
209 
210  $request = ObjectFactory::getInstance('request');
211  $token = $request->hasHeader(self::TOKEN_HEADER) ?
212  trim(str_replace(self::AUTH_TYPE, '', $request->getHeader(self::TOKEN_HEADER))) : $this->token;
213  if ($token !== null) {
214  $jwt = (new Parser())->parse((string)$token);
215 
216  // validate
217  $data = new ValidationData();
218  $data->setIssuer($this->getTokenIssuer());
219  if ($jwt->validate($data) && $jwt->verify($this->getTokenSigner(), $this->key)) {
220  $result = $jwt->getClaims();
221  }
222  }
223  return $result;
224  }
225 
226  /**
227  * Serialize a value to be used in a cookie
228  * @param $value
229  * @return String
230  */
231  protected function serializeValue($value) {
232  return json_encode($value);
233  }
234 
235  /**
236  * Unserialize a value used in a cookie
237  * @param $value
238  * @return String
239  */
240  protected function unserializeValue($value) {
241  return json_decode($value, true);
242  }
243 }
Session is the interface for session implementations and defines access to session variables.
Definition: Session.php:19
getTokenData()
Get the claims stored in the JWT.
serializeValue($value)
Serialize a value to be used in a cookie.
A session that requires clients to send a token for authentication.
unserializeValue($value)
Unserialize a value used in a cookie.
ClientSideSession has no server state as it stores the data in cookies.
StringUtil provides support for string manipulation.
Definition: StringUtil.php:18
getValue($key, $section)
Get a configuration value.
Implementations of Configuration give access to the application configuration.
static slug($string)
Converts all accent characters to ASCII characters.
Definition: StringUtil.php:419
createToken($login)
Create the token for the given login.
URIUtil provides support for uri manipulation.
Definition: URIUtil.php:18
static getInstance($name, $dynamicConfiguration=[])
__construct(Configuration $configuration)
Constructor.
static getProtocolStr()
Definition: URIUtil.php:169
ObjectFactory implements the service locator pattern by wrapping a Factory instance and providing sta...