33import json
44import logging
55import os
6+ import sys
67import time
78from http .cookies import SimpleCookie
89from urllib .parse import urlparse
1213from cryptojwt .jwe .aes import AES_GCMEncrypter
1314from cryptojwt .jwe .utils import split_ctx_and_tag
1415from cryptojwt .jwk .hmac import SYMKey
15- from cryptojwt .jwk .jwk import key_from_jwk_dict
1616from cryptojwt .jws .hmac import HMACSigner
17- from cryptojwt .key_bundle import init_key , import_jwk
17+ from cryptojwt .key_bundle import import_jwk
18+ from cryptojwt .key_bundle import init_key
1819from cryptojwt .utils import as_bytes
1920from cryptojwt .utils import as_unicode
2021from cryptojwt .utils import b64e
22+ from oidcmsg import time_util
2123from oidcmsg .time_util import in_a_while
2224
2325from oidcendpoint .util import lv_pack
3436]
3537
3638
39+ def _expiration (timeout , time_format = None ):
40+ """
41+ Return an expiration time
42+
43+ :param timeout: When
44+ :param time_format: The format of the returned value
45+ :return: A timeout date
46+ """
47+ if timeout == "now" :
48+ return time_util .instant (time_format )
49+ else :
50+ # validity time should match lifetime of assertions
51+ return time_util .in_a_while (minutes = timeout , time_format = time_format )
52+
53+
3754def sign_enc_payload (load , timestamp = 0 , sign_key = None , enc_key = None , sign_alg = "SHA256" ):
3855 """
3956
@@ -106,7 +123,7 @@ def ver_dec_content(parts, sign_key=None, enc_key=None, sign_alg="SHA256"):
106123 mac = base64 .b64decode (b64_mac )
107124 verifier = HMACSigner (algorithm = sign_alg )
108125 if verifier .verify (
109- load .encode ("utf-8" ) + timestamp .encode ("utf-8" ), mac , sign_key .key
126+ load .encode ("utf-8" ) + timestamp .encode ("utf-8" ), mac , sign_key .key
110127 ):
111128 return load , timestamp
112129 else :
@@ -125,9 +142,9 @@ def ver_dec_content(parts, sign_key=None, enc_key=None, sign_alg="SHA256"):
125142 if len (p ) == 3 :
126143 verifier = HMACSigner (algorithm = sign_alg )
127144 if verifier .verify (
128- load .encode ("utf-8" ) + timestamp .encode ("utf-8" ),
129- base64 .b64decode (p [2 ]),
130- sign_key .key ,
145+ load .encode ("utf-8" ) + timestamp .encode ("utf-8" ),
146+ base64 .b64decode (p [2 ]),
147+ sign_key .key ,
131148 ):
132149 return load , timestamp
133150 else :
@@ -136,15 +153,19 @@ def ver_dec_content(parts, sign_key=None, enc_key=None, sign_alg="SHA256"):
136153
137154
138155def make_cookie_content (
139- name ,
140- load ,
141- sign_key ,
142- domain = None ,
143- path = None ,
144- timestamp = "" ,
145- enc_key = None ,
146- max_age = 0 ,
147- sign_alg = "SHA256" ,
156+ name ,
157+ load ,
158+ sign_key ,
159+ domain = None ,
160+ path = None ,
161+ expire = 0 ,
162+ timestamp = "" ,
163+ enc_key = None ,
164+ max_age = 0 ,
165+ sign_alg = "SHA256" ,
166+ secure = True ,
167+ http_only = True ,
168+ same_site = ""
148169):
149170 """
150171 Create and return a cookies content
@@ -167,12 +188,21 @@ def make_cookie_content(
167188 :type sign_key: A :py:class:`cryptojwt.jwk.hmac.SYMKey` instance
168189 :param domain: The domain of the cookie
169190 :param path: The path specification for the cookie
191+ :param expire: Number of minutes before this cookie goes stale
192+ :type expire: int
170193 :param timestamp: A time stamp
171194 :type timestamp: text
172195 :param enc_key: The key to use for payload encryption.
173196 :type enc_key: A :py:class:`cryptojwt.jwk.hmac.SYMKey` instance
174197 :param max_age: The time in seconds for when a cookie will be deleted
175198 :type max_age: int
199+ :param secure: A secure cookie is only sent to the server with an encrypted request over the
200+ HTTPS protocol.
201+ :type secure: boolean
202+ :param http_only: HttpOnly cookies are inaccessible to JavaScript's Document.cookie API
203+ :type http_only: boolean
204+ :param same_site: Whether SameSite (None,Strict or Lax) should be added to the cookie
205+ :type same_site: byte string
176206 :return: A SimpleCookie instance
177207 """
178208 if not timestamp :
@@ -183,45 +213,70 @@ def make_cookie_content(
183213 )
184214
185215 content = {name : {"value" : _cookie_value }}
216+
186217 if path is not None :
187218 content [name ]["path" ] = path
188219 if domain is not None :
189220 content [name ]["domain" ] = domain
190-
191- content [name ]["httponly" ] = True
192-
193221 if max_age :
194222 content [name ]["expires" ] = in_a_while (seconds = max_age )
223+ if path :
224+ content [name ]["path" ] = path
225+ if domain :
226+ content [name ]["domain" ] = domain
227+ if expire :
228+ content [name ]["expires" ] = _expiration (expire , "%a, %d-%b-%Y %H:%M:%S GMT" )
229+ if same_site :
230+ content [name ]["SameSite" ] = same_site
231+
232+ # these are booleans so just set them.
233+ content [name ]["Secure" ] = secure
234+ content [name ]["httponly" ] = http_only
235+
195236
196237 return content
197238
198239
199240def make_cookie (
200- name ,
201- payload ,
202- sign_key ,
203- domain = None ,
204- path = None ,
205- timestamp = "" ,
206- enc_key = None ,
207- max_age = 0 ,
208- sign_alg = "SHA256" ,
241+ name ,
242+ payload ,
243+ sign_key ,
244+ domain = None ,
245+ path = None ,
246+ expire = 0 ,
247+ timestamp = "" ,
248+ enc_key = None ,
249+ max_age = 0 ,
250+ sign_alg = "SHA256" ,
251+ secure = True ,
252+ http_only = True ,
253+ same_site = ""
254+
209255):
210256 content = make_cookie_content (
211257 name ,
212258 payload ,
213259 sign_key ,
214260 domain = domain ,
215261 path = path ,
262+ expire = expire ,
216263 timestamp = timestamp ,
217264 enc_key = enc_key ,
218265 max_age = max_age ,
219266 sign_alg = sign_alg ,
267+ secure = secure ,
268+ http_only = http_only ,
269+ same_site = same_site
220270 )
221271
222272 cookie = SimpleCookie ()
273+
223274 for name , args in content .items ():
224275 cookie [name ] = args ["value" ]
276+ # Necessary if Python version < 3.8
277+ if sys .version_info [:2 ] <= (3 , 8 ):
278+ cookie [name ]._reserved [str ("samesite" )] = str ("SameSite" )
279+
225280 for key , value in args .items ():
226281 if key == "value" :
227282 continue
@@ -273,26 +328,20 @@ def parse_cookie(name, sign_key, kaka, enc_key=None, sign_alg="SHA256"):
273328 return ver_dec_content (parts , sign_key , enc_key , sign_alg )
274329
275330
276- def import_jwk (filename ):
277- with open (filename ) as jwk_file :
278- jwk_dict = json .loads (jwk_file .read ())
279- return key_from_jwk_dict (jwk_dict )
280-
281-
282331class CookieDealer (object ):
283332 """
284333 Functionality that an entity that deals with cookies need to have
285334 access to.
286335 """
287336
288337 def __init__ (
289- self ,
290- sign_key = "" ,
291- enc_key = "" ,
292- sign_alg = "SHA256" ,
293- default_values = None ,
294- sign_jwk = None ,
295- enc_jwk = None ,
338+ self ,
339+ sign_key = "" ,
340+ enc_key = "" ,
341+ sign_alg = "SHA256" ,
342+ default_values = None ,
343+ sign_jwk = None ,
344+ enc_jwk = None ,
296345 ):
297346
298347 if sign_key :
@@ -418,15 +467,15 @@ def get_cookie_value(self, cookie=None, cookie_name=None):
418467 return None
419468
420469 def append_cookie (
421- self ,
422- cookie ,
423- name ,
424- payload ,
425- typ ,
426- domain = None ,
427- path = None ,
428- timestamp = "" ,
429- max_age = 0 ,
470+ self ,
471+ cookie ,
472+ name ,
473+ payload ,
474+ typ ,
475+ domain = None ,
476+ path = None ,
477+ timestamp = "" ,
478+ max_age = 0 ,
430479 ):
431480 """
432481 Adds a cookie to a SimpleCookie instance
0 commit comments