italia / spid-cie-oidc-django

The SPID/CIE OIDC Federation SDK, written in Python
Apache License 2.0
23 stars 25 forks source link

Request su Authorization e lettura Token #239

Closed mirkolarocca closed 2 years ago

mirkolarocca commented 2 years ago

Ciao, stiamo usando il progetto per testare un nostro OP ma alcune cose non ci sono chiare:

  1. Nella request fatta dal RP all'authorization endpoint dell'OP, non viene inserito nell'header l'expiration del token. E'corretto o va aggiunto?
  2. Nella risposta dello userinfo endpoint da parte dell'OP, non è chiaro se va fornito un token crittografato all'interno di un token firmato oppure solo un token crittografato. Nel caso in cui fosse necessario fornire un token "nidificato" avremmo un'inconsistenza nel codice nella funzione decrypt_jwe (chiamata in spid_cie_oidc/relying_party/oidc/init.py), in quanto all'interno della stessa si prova ad eseguire un json.load mentre sarebbe necessario ritornare il JWT interno sotto forma di stringa per renderlo utilizzabile poi nella funzione unpad_jwt_head. Nel secondo caso invece le funzioni successive alla decrypt_jwe non sarebbero necessarie (unpad_jwt_head andrebbe in errore in quanto in input avrebbe un json al posto di un token).

Ti ringrazio anticipatamente per la risposta.

peppelinux commented 2 years ago

Ciao @mirkolarocca grazie per averci raggiunto qui. Vediamo un poco:

Punto 1

intendi l'header del JWT dell'id_token? exp è nel payload, dall'example project ottieni qualcosa come segue. Ho capito bene la tua domanda?

{
  "sub": "da750f319fa08358b1342d9de6156f590c8f97c6b5d33a9dfd71657e121ecc58",
  "nonce": "cv6maa3OaIyeKQqtx7VeUr1tB8QJGvH6",
  "at_hash": "aIo0TcD4HDaWPJ3OKw1JOg",
  "c_hash": "ffisORKItWx3O5LCxaqmpQ",
  "aud": [
    "http://127.0.0.1:8000/oidc/rp/"
  ],
  "iss": "http://127.0.0.1:8000/oidc/op/",
  "acr": "https://www.spid.gov.it/SpidL2",
  "jti": "aefb0002-5cdd-4da9-8e88-254fe2f51750",
  "exp": 1656008816,
  "iat": 1656006836
}

questo invece nell'access token

image

Punto 2

La risposta dello userinfo SPID o CIE è di tipo Nested JWT, ovvero firmato eppoi incapsulato in un JWT criptato. All'inizio ho arricciato il naso per questo, poi analizzando a fondo mi sono reso conto che è un ottimo meccanismo per risolvere by design il furto degli access token, solo il RP può decriptare, il bearer token torna da user info endpoint un jwt criptato.

in quanto all'interno della stessa si prova ad eseguire un json.load mentre sarebbe necessario ritornare il JWT interno sotto forma di stringa per renderlo utilizzabile poi nella funzione unpad_jwt_head. Nel secondo caso invece le funzioni successive alla decrypt_jwe non sarebbero necessarie (unpad_jwt_head andrebbe in errore in quanto in input avrebbe un json al posto di un token)

Io ho interpretato questo passaggio della specifica the response MUST be signed then encrypted, with the result being a Nested JWT, as defined in [[JWT]](https://openid.net/specs/openid-connect-core-1_0.html#JWT).

ti allego una sessione debuggata qui, come puoi vedere creo un JWS firmato che è una stringa, dopodichè la cripto in un altro JWE. Ho sbagliato qualcosa? Non ho capito il tuo commento ma mi fa piacere che qualcuno finalmente metta la testa nel codice, grazie!

[30] > /spid_cie_oidc/spid_cie_oidc/provider/views/userinfo_endpoint.py(87)get()
-> jws = create_jws(jwt, issuer.jwks_core[0])

(Pdb++) jwt
{'sub': 'da750f319fa08358b1342d9de6156f590c8f97c6b5d33a9dfd71657e121ecc58', 'https://attributes.spid.gov.it/name': 'antonio', 'https://attributes.spid.gov.it/familyName': 'rossi', 'https://attributes.spid.gov.it/email': 'antonio@email.it', 'https://attributes.spid.gov.it/fiscalNumber': 'AATTTJDFKSKDF89'}

(Pdb++) n
[30] > /spid_cie_oidc/spid_cie_oidc/provider/views/userinfo_endpoint.py(90)get()
-> jwe = encrypt_dict(jws, rp_tc.metadata['openid_relying_party']["jwks"]["keys"][0])

(Pdb++) jws
'eyJhbGciOiJSUzI1NiIsImtpZCI6ImRCNjdnTDdjazNURmlJQWY3TjZfN1NIdnFrME1EWU1FUWNvR0dsa1VBQXcifQ.eyJzdWIiOiJkYTc1MGYzMTlmYTA4MzU4YjEzNDJkOWRlNjE1NmY1OTBjOGY5N2M2YjVkMzNhOWRmZDcxNjU3ZTEyMWVjYzU4IiwiaHR0cHM6Ly9hdHRyaWJ1dGVzLnNwaWQuZ292Lml0L25hbWUiOiJhbnRvbmlvIiwiaHR0cHM6Ly9hdHRyaWJ1dGVzLnNwaWQuZ292Lml0L2ZhbWlseU5hbWUiOiJyb3NzaSIsImh0dHBzOi8vYXR0cmlidXRlcy5zcGlkLmdvdi5pdC9lbWFpbCI6ImFudG9uaW9AZW1haWwuaXQiLCJodHRwczovL2F0dHJpYnV0ZXMuc3BpZC5nb3YuaXQvZmlzY2FsTnVtYmVyIjoiQUFUVFRKREZLU0tERjg5In0.k36HqIdUvW2KlbVT9YY5EKrdlzP-n7ZbWEd-CKcFnP5p7ovIynF2SHJ55gPCHw813Ud-aOqCvgi9QZMxnNcEDTBMTiGqTDYqWMgoMS8DVXaMmgE1v8id7UMyNOsk_W7E27Qvm6w04ltlC_FEX4y8ZOqaIP-l2ahs4JsCsqKhTKnxpQCrqoy5Dn5d74JKmMOJPBE_JpJ22Eze9zy3iGmCUnSyRH9pEpwrDi2CwBIC-k0q-Sa_vMsEQcRV6xiwqYaB5BDaMAGDHBS2Q_YxxlLri4HRqCd8JRKfGtwxuTNoopOq8r8c8_qUHZrR1DBhb_pDAyYT8TVLE8mGFPzgbFBN8A'
(Pdb++) n

[30] > /spid_cie_oidc/spid_cie_oidc/provider/views/userinfo_endpoint.py(91)get()
-> return HttpResponse(jwe, content_type="application/jose")

(Pdb++) jwe
'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJraWQiOiIySG5vRlMzWW5DOXRqaUNhaXZoV0xWVUozQXh3R0d6Xzk4dVJGYXFNRUVzIn0.QamhUPwcZfphIgL3WyNlGtPy-Kg1NLCqXbuqxwLv29y6Zyxij5c6S0cq_p2LQVWWppTyuZZLgEfgo_DNTENFfuNeaM0seOB2omo85ypKeMJvNnUFd9YL5cpSsFVnpnUahxUmcvgtc-Iugi1L-v_T8aRaJGVzWs-gkiBs1lEFqEesi_O6dQ5ilr4yxYnQbESXBv45wbEctWcGPUGgVqoQSS1C2FVNBEZfjaDiNvKq7fp7W861-U9nJrcJpofJ0QsPf_Q36quk9tglvn0PhGGfZYYsFFRj6SL4EmRwxHgLYt2G35wE94WPVdg5fZYPiz39NihZVodPMpS4izffeHfP6Q.B14jBNlDliMYERGsJR4RcQ.VDXwrIfBQCJjTJqFoVgCrHpajiTJovOM2Y3zIeRX0Y5zchGJlauZ4ILaiD_MLKOKxGUNW_tpes4dHg3f3Wf9VwGolAxIEz1p4kDGw2ArsXI5G0i7_FgFeQyodhDJC9TUrImj96eZ3obb0-Nn-F0oEZJCbocPcD9sbMR5IqCsl8_Y7ptWRoArCmdx81355UuhHwqRgJHnhyZPzjSPFhHgh1kBSRTiGHi2JcfytyY1PypWv_RPJhYlFG6PBKZm_zF3bGr_DpDG0OrUu4Tm4UF0Cu4SzDSJjJbjMeFVCdb_cQdycnawprNOuHM8rwLFxuClBVs446oLU0R5tFExEBSgO17qAVFZozJL8ynJmCQV92rybXVxU9uOh5jW8Z7Lvsblh302CJ-B4pCtSOqZ6dmgr0CGbN3fh8ujIrgdMd-OCkzDydOSpOMKeRsfHxMxweeSxDYaXAvrFlf4uAhp5QrwbRqMmJ7Wc3R4yAhNG2J5P43BGlGv72Kb5DIhTFzDBc0xpOyzHTYBHferlSvUSEHSFYAtA302pNT1-T5aZgwumUm9dYDlsMKB24HMFmfhAlTrtRx0qG_O3lnUfXIn8Abzxg4JgXLNBh_HYVSehCBRrWBqGuprW74fV_R71rdgjqgPVwTI27Zt2J0DntUSPglHfdhE3Jdhm6Kq7XSaqmXzW7M6jDm1PTP7CCCnAQ8zDUiNB3k9qYTR5iKVCXN8xYA2J2tA7ChkCaFNISn95UUuLGPVOXafxKxfnSQGM_TUxVdmVno1FqI_-WRuEPmOt0DoXXBN5pA4sxyubCkBKUtrExOF9apjqplRdgmN4tceCPEjnhQn6NB8p0rGJFWEgoKeGYw6EegDxWK2gps74amRi9rqBW164PtXCiDFTTeejTEePFkr4VStYEOEgN0gAKmexQJ5J2Tim18LIhrVrGYSnF1AS49TQc27kRA9-VUmWo67ZO84we-052oI1K0iiLzWbMfkdZc0daY_3p_GKsYfhI1RymVjLUfGMwWBfmROCu6Ua_Idvuk48EV8qz9eDzz02a2OghZFtd5BHtW3rvHMruCmuIglnk3-eprUu6oaLrNvVANUs_XaTOM46BFtQEVShA.fNjgu3fQR_5h98D4v7kqK1NXHkMcx7zjZzx1_-QHin4'
peppelinux commented 2 years ago

Forse ti riferisci al codice del RP? dammi un link del codice così lo vediamo insieme

peppelinux commented 2 years ago

Ciao @mirkolarocca hai aggiornamenti oppure possiamo ritenere questa issue come chiusa?

peppelinux commented 2 years ago

Credo che il problema emerso in questa issue sia dovuto ad una cattiva semantica nel codice. Con questo commit credo di aver risolto eventuali dubbi

https://github.com/italia/spid-cie-oidc-django/commit/158840b776945f9acc5688f087148ed570c7e46d

@mirkolarocca attendo tua conferma