Skip to content

Commit da0076c

Browse files
committed
Allow Fernet crypto to be used with CookieHandler.
1 parent 8eedbec commit da0076c

4 files changed

Lines changed: 77 additions & 2 deletions

File tree

src/idpyoidc/server/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from cryptojwt import KeyJar
88

99
from idpyoidc.impexp import ImpExp
10-
from idpyoidc.message.oidc import RegistrationRequest
1110
from idpyoidc.server import authz
1211
from idpyoidc.server.client_authn import client_auth_setup
1312
from idpyoidc.server.configure import ASConfiguration

src/idpyoidc/server/cookie_handler.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
from cryptojwt.jws.hmac import HMACSigner
1616
from cryptojwt.jwt import utc_time_sans_frac
1717
from cryptojwt.key_jar import init_key_jar
18+
from cryptojwt.utils import as_bytes
1819

20+
from idpyoidc.encrypter import init_encrypter
1921
from idpyoidc.server.util import lv_pack
2022
from idpyoidc.server.util import lv_unpack
2123
from idpyoidc.time_util import epoch_in_a_while
@@ -37,10 +39,12 @@ def __init__(
3739
keys: Optional[dict] = None,
3840
sign_alg: [str] = "SHA256",
3941
name: Optional[dict] = None,
42+
crypt_config: Optional[dict] = None,
4043
**kwargs,
4144
):
4245
self.sign_key = None
4346
self.enc_key = None
47+
self.crypt = None
4448

4549
if keys:
4650
key_jar = init_key_jar(**keys)
@@ -50,6 +54,10 @@ def __init__(
5054
_keys = key_jar.get_encrypt_key(key_type="oct")
5155
if _keys:
5256
self.enc_key = _keys[0]
57+
elif crypt_config:
58+
_crypt = init_encrypter(crypt_config)
59+
self.crypt = _crypt["encrypter"]
60+
self.crypt_config = _crypt["conf"]
5361
else:
5462
if sign_key:
5563
if isinstance(sign_key, SYMKey):
@@ -134,6 +142,11 @@ def _sign_enc_payload(self, payload: str, timestamp: Optional[Union[int, str]] =
134142
base64.b64encode(ctx),
135143
base64.b64encode(tag),
136144
]
145+
elif self.crypt:
146+
msg = lv_pack(timestamp, payload)
147+
cookie_payload = [
148+
bytes_timestamp,
149+
base64.b64encode(self.crypt.encrypt(msg.encode()))]
137150
else:
138151
cookie_payload = [bytes_timestamp, bytes_load, base64.b64encode(mac)]
139152

@@ -149,6 +162,15 @@ def _ver_dec_content(self, parts):
149162

150163
if parts is None:
151164
return None
165+
elif len(parts) == 2:
166+
t0, enc_payload = parts
167+
if not self.crypt:
168+
raise VerificationError("Can not decrypt")
169+
msg = self.crypt.decrypt(base64.b64decode(as_bytes(enc_payload)))
170+
t1, payload = lv_unpack(msg.decode("utf-8"))
171+
if t0 != t1:
172+
raise VerificationError('Suspicious timestamp')
173+
return payload, t1
152174
elif len(parts) == 3:
153175
# verify the cookie signature
154176
timestamp, payload, b64_mac = parts

tests/private/cookie_jwks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"keys": [{"kty": "oct", "use": "enc", "kid": "enc", "k": "XlZGhRrHFOQwgIJZSV8g23cxVoL27xyv"}, {"kty": "oct", "use": "sig", "kid": "sig", "k": "TD6UZ7GmkUeC1oejdCGTf2YAp7FHeGfd"}]}
1+
{"keys": [{"kty": "oct", "use": "enc", "kid": "enc", "k": "G6JN24md0JKKBD16Aq02wPpvW6QrlgzI"}, {"kty": "oct", "use": "sig", "kid": "sig", "k": "8YmUB_TM3eW3-uZHUqgsuiXSkMpgboUk"}]}

tests/test_server_20g_cookie_handler.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from idpyoidc.server.cookie_handler import CookieHandler
55
from idpyoidc.server.cookie_handler import compute_session_state
6+
from tests import CRYPT_CONFIG
67

78
KEYDEFS = [
89
{"type": "OCT", "kid": "sig", "use": ["sig"]},
@@ -233,3 +234,56 @@ def test_mult_cookie(self):
233234
def test_compute_session_state():
234235
hv = compute_session_state("state", "salt", "client_id", "https://example.com/redirect")
235236
assert hv == "d21113fbe4b54661ae45f3a3233b0f865ccc646af248274b6fa5664267540e29.salt"
237+
238+
239+
class TestCookieHandlerFernetEnc(object):
240+
@pytest.fixture(autouse=True)
241+
def make_cookie_content_handler(self):
242+
cookie_conf = {
243+
"crypt_config": CRYPT_CONFIG,
244+
}
245+
246+
self.cookie_handler = CookieHandler(**cookie_conf)
247+
248+
def test_make_cookie_content(self):
249+
_cookie_info = self.cookie_handler.make_cookie_content("idpyoidc.server", "value", "sso")
250+
assert _cookie_info
251+
assert set(_cookie_info.keys()) == {"name", "value", "samesite", "httponly", "secure"}
252+
assert len(_cookie_info["value"].split("|")) == 2
253+
254+
def test_make_cookie_content_max_age(self):
255+
_cookie_info = self.cookie_handler.make_cookie_content(
256+
"idpyoidc.server", "value", "sso", max_age=3600
257+
)
258+
assert _cookie_info
259+
assert set(_cookie_info.keys()) == {
260+
"name",
261+
"value",
262+
"max-age",
263+
"samesite",
264+
"httponly",
265+
"secure",
266+
}
267+
assert len(_cookie_info["value"].split("|")) == 2
268+
269+
def test_read_cookie_info(self):
270+
_cookie_info = [self.cookie_handler.make_cookie_content("idpyoidc.server", "value", "sso")]
271+
returned = [{"name": c["name"], "value": c["value"]} for c in _cookie_info]
272+
_info = self.cookie_handler.parse_cookie("idpyoidc.server", returned)
273+
assert len(_info) == 1
274+
assert set(_info[0].keys()) == {"value", "type", "timestamp"}
275+
assert _info[0]["value"] == "value"
276+
assert _info[0]["type"] == "sso"
277+
278+
def test_mult_cookie(self):
279+
_cookie = [
280+
self.cookie_handler.make_cookie_content("idpyoidc.server", "value", "sso"),
281+
self.cookie_handler.make_cookie_content("idpyoidc.server", "session_state", "session"),
282+
]
283+
assert len(_cookie) == 2
284+
_c_info = self.cookie_handler.parse_cookie("idpyoidc.server", _cookie)
285+
assert len(_c_info) == 2
286+
assert _c_info[0]["value"] == "value"
287+
assert _c_info[0]["type"] == "sso"
288+
assert _c_info[1]["value"] == "session_state"
289+
assert _c_info[1]["type"] == "session"

0 commit comments

Comments
 (0)