Repository: italia/spid-cie-oidc-django (language: python)

Issue:
Doc onboarding
update documentation:

- registration new entity
- enable it as descendant


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 0e18f94d..c0173baf 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ each of these can be installed separately within a django project. These are the
 ------------------------------------------------
 
 ![RP Auth demo](docs/images/rp_auth_demo_3.gif)
-_An onboarded Relying Party with a succesful authentication._
+_An onboarded Relying Party with a succesfull authentication._
 
 ## Setup
 
diff --git a/docs/CREATE_A_FEDERATION.md b/docs/CREATE_A_FEDERATION.md
index 0fa6ec0d..339f4b6d 100644
--- a/docs/CREATE_A_FEDERATION.md
+++ b/docs/CREATE_A_FEDERATION.md
@@ -158,6 +158,7 @@ An example of metadata[here](https://github.com/italia/spid-cie-oidc-django/blob
 
 ![RP federation entity](images/rp_federation_entity.png)
 
+## Register an entity as descendant
 
 Now that you configured your Provider Entity you have to register it in the federation.
 Access to the Federation Authority admin backend and configure your RP as descendant and assign to it a profile.
diff --git a/docs/FEDERATION_ENTITY_API.md b/docs/FEDERATION_ENTITY_API.md
index 1824d714..ffcc9959 100644
--- a/docs/FEDERATION_ENTITY_API.md
+++ b/docs/FEDERATION_ENTITY_API.md
@@ -39,7 +39,6 @@ To evaluate a TrustChain we need at least of three things:
 - __subject identifier__ of the entity to which we want to establish the trust, the leaf.
 - __trust_anchor__, subject identifier of the Federation authority that represent the Federation,
 the root of the trust and the point of arrival, the final destination, of the path of trust.
-- __metadata_type__, which kind of metadata we want to export from the trust chain, if openid provider or relying party.
 The final metadata will be combined with the metadata policy of the superiors statements.
 - __required_trust_marks__, which trust marks are required to start a metadata discovery on the subject of the trust chain.
 At least one MUST be present as valid in the entity configuration of the subject. The validation of the trust mark JWS must be done
diff --git a/docs/images/enable_descendant.png b/docs/images/enable_descendant.png
new file mode 100644
index 00000000..76b96eb9
Binary files /dev/null and b/docs/images/enable_descendant.png differ
diff --git a/docs/images/fillregistration.png b/docs/images/fillregistration.png
new file mode 100644
index 00000000..78309e13
Binary files /dev/null and b/docs/images/fillregistration.png differ
diff --git a/docs/images/listentity.png b/docs/images/listentity.png
new file mode 100644
index 00000000..7439fb37
Binary files /dev/null and b/docs/images/listentity.png differ
diff --git a/docs/images/onboarding_landing.png b/docs/images/onboarding_landing.png
new file mode 100644
index 00000000..2e18f97d
Binary files /dev/null and b/docs/images/onboarding_landing.png differ
diff --git a/docs/images/profiles_op.png b/docs/images/profiles_op.png
deleted file mode 100644
index e69de29b..00000000
diff --git a/docs/technical_specifications/AUTHORITY.md b/docs/technical_specifications/AUTHORITY.md
index d9906319..0ab0bdf0 100644
--- a/docs/technical_specifications/AUTHORITY.md
+++ b/docs/technical_specifications/AUTHORITY.md
@@ -38,11 +38,51 @@ Lists all the descendant entities.
  - `http://127.0.0.1:8000/list/?type=openid_provider`
 
 
+#### Advanced entity listing endpoint
+
+The advanced entity listing endpoint extends Listings endpoint.
+
+The request MUST be an HTTP request using the GET method, an entity needs to know only the endpoint url.
+
+Example of Advanced listing request: `http://127.0.0.1:8000/advanced_entity_listing/`
+
+
+The response is a json object (content type: "application/json") in which for each entity the only REQUIRED claim is iat.
+The entity's result set must have a descendant ordering with higher iat on top.
+
+A response example: 
+````
+{
+    "iss": "https://registry.spid.gov.it/",
+    "iat": 1620050972,
+    "entities": [
+        {
+            "https://rp.example.it/spid/": {
+            "iat": 1588455866,
+            },
+            {
+            "https://rp.another.it/spid/": {
+            "iat": 1588455856,
+            },
+            {
+            "https://rp.it/spid/": {
+            "iat": 1588355866,
+            },
+        ... # many other entries
+    ],
+    "page": 1,
+    "total_pages": 2,
+    "total_entries": 189,
+    "next_page_path": "/federation_adv_list/?page=2",
+    "prev_page_path": ""
+}
+```` 
+
 #### Resolve entity statement
 
 An entity MAY use the resolve endpoint to fetch resolved metadata and trust marks for an entity as seen/trusted by the resolver. 
 
-- `http://127.0.0.1:8000/resolve/?sub=http://127.0.0.1:8000/oidc/op/&anchor=http://127.0.0.1:8000/&format=json`
+- `http://127.0.0.1:8000/resolve/?sub=http://127.0.0.1:8000/oidc/rp/&anchor=http://127.0.0.1:8000/&type=openid_relying_party&format=json`
 - `http://127.0.0.1:8000/resolve/?sub=http://127.0.0.1:8000/oidc/op/&anchor=http://127.0.0.1:8000/`
 
 
diff --git a/docs/technical_specifications/ONBOARDING.md b/docs/technical_specifications/ONBOARDING.md
index a013ee9e..b53ba0a7 100644
--- a/docs/technical_specifications/ONBOARDING.md
+++ b/docs/technical_specifications/ONBOARDING.md
@@ -2,82 +2,65 @@
 
 > ⚠️ This application is only intended for demostration purpose and not for production use.
 
-
 ## General OnBoarding registration flow
 
 The actions to accredit an entity within the demo federation are described below.
 
 ### Entity Registration
 
-1. the legal representative of an entity/org accesses via SPID or CIE
-   - __demo__ web resource `/onboarding/landing/`
-   - __warning__ this onboarding demo comes with an unprotected registration page.
-   The SPID and CIE authentication won't be involved in this demo.
-
-2. becomes recognizable as an org affiliate and is given privileges to operate on
-behalf of that org for the registration of an entity, if not already present within the federation.
-
-
-3. The user accesses the submission form of a federation entity, fill in the following REQUIRED fields:
-    1. Organization Name (String)
-    2. unique identifier of the entity (url) of the instance in production/stage (URL)
-        1. validators:
-            - download of the Entity Configuration, a self-signed statement
-            - JWS signature validation
-            - structure analysis of the JSON (json-schema)
-            - presence and validation of the claim  __authority_hints__ that MUST matches to the onboarding system entity (trust anchor)
-    3. IPA code or VAT number, that determines whether the participant is public or private (String)
-        1. validators:
-            - TODO: fetch and verify the IPA code from the national registry of public services
-    4. public jwks (List[JSON])
-        1. validators:
-            - check if the jwks is public and not private (for sake!)
-            - check of the presence of kid claim
-            - check that the certificate is not expired
-            - check that the kid is unique and of the lenght of a minimum JWK thumbprint length
-    5. at least one administrative email contact (String)
-        1. validators:
-            - Email Field
-    6. at least one administrative email contact (String)
-        1. validators:
-            - Email Field
-    7. URL where the RP page shows the SPID and CIE authentication button
-        1. validators:
-            - Null, just for demo purpose
-    8. SPID/CIE authentication request trigger url at the onboarding system testing OP
-        1. validators:
-            - Null, just for demo purpose
-
-### SPID/CIE QaD tests
-
-Once the submission passes the initial checks the request is saved and a batch will start the
-Automatic checks on the latter new registered entity. These check covers
-
-- entity configuration:
-    - reachability
-    - signature validation
-    - best practices checks following AgID and IPZS OIDC Fed guidelines.
-
-- RP authz check following AgID and IPZS OIDC Fed guidelines.
-- trust marks forgery (if present for recurrent sample checks)
-
-The tests produces a json report.
-this report would be also available in HTML format.
-
-### Entity OnBoarding
-
-- all the required information are moved (copied) from the OnBoardin registration storage to the FederationDescendant storage
-- trust marks is automatically generated for the available profile, public or private
-- the onboarding staff in any time can add more profiles and trust marks  through the FederationDescendant back office panel
-- an entity configuration with trust marks and metadata policy applied, signed by the Trust Anchor of the onboarding system, is automatically created and published to its __Resolve Entity Statement__ endpoint.
-- an email is sent to the user to inform on the succesfull state of the onboarding and the final entity configuration
-
-
-### Considerations
-
-1. it would be necessary to "regulate" how the entities must expose a trigger url of the authn request.
-This would allow us to batch automate testing and obtain asynchronous reports and request states.
-Automatically a warning email would be sent to users.
-
-2. It would be useful to standardize a "quota" to avoid that "the usual primers"
-can make too many stupid requests all at once, that is, max N batch testing can be performed per day for a single applicant.
+To accredit a new entity point your browser to /onboarding/landing and click on the button `Register your entity`.
+![Onboarding landing](../images/onboarding_landing.png)
+
+The user accesses the submission form of a federation entity, fill in the following REQUIRED fields:
+
+1. Organization Name (String)
+2. unique identifier of the entity (url) of the instance in production/stage (URL)
+    1. validators:
+        - download of the Entity Configuration, a self-signed statement
+        - JWS signature validation
+        - structure analysis of the JSON (json-schema)
+        - presence and validation of the claim  __authority_hints__ that MUST matches to the onboarding system entity (trust anchor)
+3. TODO: IPA code or VAT number, that determines whether the participant is public or private (String)
+    1. validators:
+        - TODO: fetch and verify the IPA code from the national registry of public services
+4. public jwks (List[JSON])
+    1. validators:
+        - check if the jwks is public and not private (for sake!)
+        - check of the presence of kid claim
+        - check that the certificate is not expired
+        - check that the kid is unique and of the lenght of a minimum JWK thumbprint length
+5. TODO: at least one email contact (String)
+    1. validators:
+        - Email Field
+6. URL where the RP page shows the SPID and CIE authentication button
+    1. validators:
+        - Null, just for demo purpose
+7. TODO: SPID/CIE authentication request trigger url at the onboarding system testing OP
+    1. validators:
+        - Null, just for demo purpose
+
+![Registration entity](../images/fillregistration.png)
+
+After a successful submission the user is redirected to the list of registered entities.
+
+![entity list](../images/listentity.png)
+
+## The Federation Operator enables the entity as descendant
+
+- from the admin panel a staff user can enable the entity as a FederationDescendant
+- all the required information are moved (copied) from the OnBoarding registration 
+- the staff user assigns a Descendant Profile (trust mark) to the onboarded entity
+ (See the example in the documentation [CREATE_A_FEDERATION.md](../CREATE_A_FEDERATION.md#register-an-entity-as-descendant) )
+ see "Register an entity as descendant".
+- the staff user in any time can add more profiles and trust marks through the FederationDescendant back office panel
+- an entity configuration with trust marks and metadata policy applied, signed by the Trust Anchor of the onboarding system,
+ is now available to its __Resolve Entity Statement__ endpoint.
+
+To enable the onboarded entity, the Federation Operator points its web browser to the admin panel:
+
+- Click OnBoarding Registrations
+- Select the entity you want to enable 
+- Select action "enable descendant"
+- Submit
+
+![admin enable descendant](../images/enable_descendant.png)
diff --git a/docs/technical_specifications/PROVIDER.md b/docs/technical_specifications/PROVIDER.md
index 47d72374..e072638b 100644
--- a/docs/technical_specifications/PROVIDER.md
+++ b/docs/technical_specifications/PROVIDER.md
@@ -120,8 +120,8 @@ OIDCFED_PROVIDER_AUTH_CODE_MAX_AGE = getattr(
 Example
 ````
 OIDCFED_PROVIDER_PROFILES_DEFAULT_ACR = dict(
-    spid = AcrValuesSpid.l2.value,
-    cie = AcrValuesCie.l2.value
+    spid = AcrValues.l2.value,
+    cie = AcrValues.l2.value
 )
 ````
 
@@ -190,7 +190,7 @@ The UserInfo Endpoint returns an encrypthed jwt of the user claims.
 
 ### Login history page
 
-![OIDC Provider login history](docs/images/provider_login_history.png)
+![OIDC Provider login history](../images/provider_login_history.png)
 _The user can consult the history of his accesses and also can revoke the access tokens for selected RPs._
 
 ## SPID/CIE QAD and compliances tests
diff --git a/docs/technical_specifications/RELYING_PARTY.md b/docs/technical_specifications/RELYING_PARTY.md
index 7a6371c5..0414c550 100644
--- a/docs/technical_specifications/RELYING_PARTY.md
+++ b/docs/technical_specifications/RELYING_PARTY.md
@@ -71,13 +71,13 @@ RP_PROVIDER_PROFILES = getattr(
     "RP_PROVIDER_PROFILES",
     {
         "spid": {
-            "authorization_request": {"acr_values": AcrValuesSpid.l2.value},
+            "authorization_request": {"acr_values": AcrValues.l2.value},
             "rp_metadata": RPMetadataSpid,
             "authn_response": AuthenticationResponse,
             "token_response": TokenResponse
         },
         "cie": {
-            "authorization_request": {"acr_values": AcrValuesCie.l2.value},
+            "authorization_request": {"acr_values": AcrValues.l2.value},
             "rp_metadata": RPMetadataCie,
             "authn_response": AuthenticationResponseCie,
             "token_response": TokenResponse
@@ -136,10 +136,7 @@ LOGIN_REDIRECT_URL = "/oidc/rp/echo_attributes"
 `OIDCFED_ACR_PROFILES`, acr value required to OP Authentication Endpoint
 Example
 ````
-    dict(
-        spid = AcrValuesSpid.l2.value,
-        cie = AcrValuesCie.l2.value
-     )
+`OIDCFED_ACR_PROFILES` = AcrValues.l2.value
 ````
 
 `RP_DEFAULT_PROVIDER_PROFILES`, default profile for OP
@@ -173,9 +170,9 @@ As inherited from [__spid_cie_oidc.entity__](docs/tecnhical_specifications/ENTIT
 
 This endpoint is the starting point for OIDC SPID/CIE authentication.
 
-the webpath is customizable in the `urls.py` file and by default it's
+The webpath is customizable in the `urls.py` file and by default it's
 configured [here](https://github.com/peppelinux/spid-cie-oidc-django/blob/main/spid_cie_oidc/relying_party/urls.py#L13) 
-and correspond to `spid_cie_oidc.relying_party.views.SpidCieOidcRpBeginView`.
+and correspond to `spid_cie_oidc.relying_party.views.rp_begin.SpidCieOidcRpBeginView`.
 
 The request is of type GET and supports the following parameters:
 
@@ -189,11 +186,42 @@ The request is of type GET and supports the following parameters:
 
 ### auth code redirect
 
-WiP
+This endpoint corresponds to the redirect uri where the auth code lands.
+
+The webpath is customizable in the `urls.py` file and by default it's
+configured [here](https://github.com/italia/spid-cie-oidc-django/blob/main/spid_cie_oidc/relying_party/urls.py#L19) 
+and correspond to `spid_cie_oidc.relying_party.views.rp_callback.SpidCieOidcRpCallbackView`.
+
+This endpoint accepts a request with this parameters:
+
+- __code__, REQUIRED. Authorization code
+- __state__, REQUIRED. State value enclosed in the authentication request
+- __iss__, REQUIRED only for cie. Issuer identifier of the OP
+
+This endpoint first call OP's token endpoint and, after getting the access token, it calls the OP's userinfo endpont.
+
+The request to token endpoint is of type POST and supports the following parameters:
+
+- __client_id__, REQUIRED. RP's client id
+- __client_assertion__, REQUIRED. JWT signed by using the private key of the RP
+- __client_assertion_type__, REQUIRED.
+- __code__, REQUIRED. Authorization code recived from authorization endpoint
+- __code_verifier__, REQUIRED
+- __grant_type__, REQUIRED. Specified the way RP gets the OIDC-Core tokens. It may be "authorization_code" or "refresh_token"
+- __refresh_token__, REQUIRED only if grant_type is refresh_token
+
+The request to userinfo endpoint is of type GET and access token is sent using the authorization header field.
+
 
 ### logout
 
-WiP
+This endpoint calls the token revocation endpoint of the op.
+
+The webpath is customizable in the `urls.py` file and by default it's
+configured [here](https://github.com/italia/spid-cie-oidc-django/blob/main/spid_cie_oidc/relying_party/urls.py#L36) 
+and correspond to `spid_cie_oidc.relying_party.views.rp_initiated_logout.oidc_rpinitiated_logout`.
+
+The request contains user to logout
 
 ## SPID/CIE QAD and compliances tests
 
diff --git a/examples/federation_authority/dumps/example.json b/examples/federation_authority/dumps/example.json
index 861435fb..39538eeb 100644
--- a/examples/federation_authority/dumps/example.json
+++ b/examples/federation_authority/dumps/example.json
@@ -928,7 +928,6 @@
     "modified": "2022-03-01T17:49:22.983Z",
     "sub": "http://127.0.0.1:8000/oidc/op/",
     "trust_anchor": 107,
-    "type": "openid_provider",
     "exp": "2022-03-18T17:05:57Z",
     "iat": "2022-02-25T20:24:45.840Z",
     "chain": [
@@ -1261,6 +1260,8 @@
       ]
     ],
     "metadata": {
+        "openid_provider":
+    {
       "authorization_endpoint": "http://127.0.0.1:8000/oidc/op/authorization",
       "revocation_endpoint": "http://127.0.0.1:8000/oidc/op/revocation/",
       "id_token_encryption_alg_values_supported": [
@@ -1409,7 +1410,7 @@
         "ES384",
         "ES512"
       ]
-    },
+    }},
     "trust_marks": [],
     "parties_involved": [
       "http://127.0.0.1:8000/oidc/op/",
@@ -1429,7 +1430,6 @@
     "modified": "2022-02-26T19:53:36.683Z",
     "sub": "http://127.0.0.1:8000/oidc/rp/",
     "trust_anchor": 107,
-    "type": "openid_relying_party",
     "exp": "2022-03-18T17:05:57Z",
     "iat": "2022-02-26T19:53:36.683Z",
     "chain": [
@@ -1586,6 +1586,7 @@
       ]
     ],
     "metadata": {
+        "openid_relying_party": {
       "application_type": "web",
       "client_id": "http://127.0.0.1:8000/oidc/rp/",
       "client_registration_types": [
@@ -1617,7 +1618,7 @@
         "code"
       ],
       "subject_type": "pairwise"
-    },
+    }},
     "trust_marks": [],
     "parties_involved": [
       "http://127.0.0.1:8000/oidc/rp/",
diff --git a/examples/provider/dumps/example.json b/examples/provider/dumps/example.json
index 4103ebee..9c974ec6 100644
--- a/examples/provider/dumps/example.json
+++ b/examples/provider/dumps/example.json
@@ -4,7 +4,7 @@
   "pk": 1,
   "fields": {
     "password": "pbkdf2_sha256$320000$FIyN55FCASEozeA1pWYRm1$8QzndgdnJkC7OPhEGPVHrrAxsa5KDoydMkl4zgrn0fs=",
-    "last_login": "2022-03-19T22:44:31.621Z",
+    "last_login": "2022-03-23T16:07:58.963Z",
     "is_superuser": true,
     "username": "admin",
     "is_staff": true,
@@ -66,7 +66,7 @@
   "pk": 1,
   "fields": {
     "created": "2022-03-16T09:48:07.724Z",
-    "modified": "2022-03-19T22:46:17.178Z",
+    "modified": "2022-03-23T16:12:05.478Z",
     "uuid": "dca85f48-0dbe-4b35-b74d-9ed42f9d00a7",
     "sub": "http://127.0.0.1:8002/oidc/op/",
     "default_exp": 2880,
@@ -119,9 +119,9 @@
           ]
         },
         "acr_values_supported": [
-          "CIE_L1",
-          "CIE_L2",
-          "CIE_L3"
+          "https://www.spid.gov.it/SpidL1",
+          "https://www.spid.gov.it/SpidL2",
+          "https://www.spid.gov.it/SpidL3"
         ],
         "claims_supported": [
           "given_name",
@@ -145,9 +145,6 @@
           "ES256"
         ],
         "issuer": "http://127.0.0.1:8002/oidc/op/",
-        "authority_hints": [
-          "http://127.0.0.1:8000/"
-        ],
         "jwks": {
           "keys": [
             {
@@ -163,7 +160,7 @@
           "openid",
           "offline_access"
         ],
-        "logo_uri": "http://127.0.0.1:8002/static/image/logo-cie-png",
+        "logo_uri": "http://127.0.0.1:8002/static/images/logo-cie.png",
         "organization_name": "SPID OIDC identity provider",
         "op_policy_uri": "http://127.0.0.1:8002/oidc/op/en/website/legal-information/",
         "request_parameter_supported": true,
@@ -244,5 +241,731 @@
     "constraints": {},
     "is_active": true
   }
+},
+{
+  "model": "spid_cie_oidc_entity.fetchedentitystatement",
+  "pk": 12,
+  "fields": {
+    "created": "2022-03-18T08:40:57.339Z",
+    "modified": "2022-03-18T08:40:57.339Z",
+    "iss": "http://127.0.0.1:8000/",
+    "sub": "http://127.0.0.1:8000/",
+    "exp": "2022-03-21T13:35:12Z",
+    "iat": "2022-03-21T13:02:12Z",
+    "statement": {
+      "exp": 1647869712,
+      "iat": 1647867732,
+      "iss": "http://127.0.0.1:8000/",
+      "sub": "http://127.0.0.1:8000/",
+      "jwks": {
+        "keys": [
+          {
+            "kty": "RSA",
+            "n": "3i5vV-_4nF_ES1BU86Zf2Bj6SiyGdGM3Izc2GrvtknQQCzpT3QlGv2d_wMrzVTS7PmZlvjyi2Qceq8EmEwbsIa5R8G57fxSpE0HL33giJfhpe8ublY4hGb6tEqSbHiFcgiF4T-Ft_98pz4nZtKTcesMZ8CcDUd9ibaLXGM4vaiUhSt76X1qOzqJHqAKMG-9VGm5DD2GSe7cu1yvaMCMPU6DGOqHYoBSkSbsnLelsRg6sINh6mZfb39odTJlOMFGhlg665702kc_iqqxd8jpyOh94vBagmJB4EQqI1qEte8sTMeBkVRpSLDoV5uNTlp2ZdINu1SakmaHB3WeStwC1lw",
+            "e": "AQAB",
+            "kid": "FifYx03bnosD8m6gYQIfNHNP9cM_Sam9Tc5nLloIIrc"
+          }
+        ]
+      },
+      "metadata": {
+        "federation_entity": {
+          "contacts": [
+            "ops@localhost"
+          ],
+          "federation_fetch_endpoint": "http://127.0.0.1:8000/fetch/",
+          "federation_resolve_endpoint": "http://127.0.0.1:8000/resolve/",
+          "federation_status_endpoint": "http://127.0.0.1:8000/trust_mark_status/",
+          "homepage_uri": "http://127.0.0.1:8000",
+          "name": "example TA",
+          "federation_list_endpoint": "http://127.0.0.1:8000/list/"
+        }
+      },
+      "trust_marks_issuers": {
+        "https://www.spid.gov.it/certification/rp/public": [
+          "https://registry.spid.agid.gov.it",
+          "https://public.intermediary.spid.it"
+        ],
+        "https://www.spid.gov.it/certification/rp/private": [
+          "https://registry.spid.agid.gov.it",
+          "https://private.other.intermediary.it"
+        ],
+        "https://sgd.aa.it/onboarding": [
+          "https://sgd.aa.it"
+        ]
+      },
+      "constraints": {
+        "max_path_length": 1
+      }
+    },
+    "jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCJ9.eyJleHAiOjE2NDc4Njk3MTIsImlhdCI6MTY0Nzg2NzczMiwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwLyIsInN1YiI6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMC8iLCJqd2tzIjp7ImtleXMiOlt7Imt0eSI6IlJTQSIsIm4iOiIzaTV2Vi1fNG5GX0VTMUJVODZaZjJCajZTaXlHZEdNM0l6YzJHcnZ0a25RUUN6cFQzUWxHdjJkX3dNcnpWVFM3UG1abHZqeWkyUWNlcThFbUV3YnNJYTVSOEc1N2Z4U3BFMEhMMzNnaUpmaHBlOHVibFk0aEdiNnRFcVNiSGlGY2dpRjRULUZ0Xzk4cHo0blp0S1RjZXNNWjhDY0RVZDlpYmFMWEdNNHZhaVVoU3Q3NlgxcU96cUpIcUFLTUctOVZHbTVERDJHU2U3Y3UxeXZhTUNNUFU2REdPcUhZb0JTa1Nic25MZWxzUmc2c0lOaDZtWmZiMzlvZFRKbE9NRkdobGc2NjU3MDJrY19pcXF4ZDhqcHlPaDk0dkJhZ21KQjRFUXFJMXFFdGU4c1RNZUJrVlJwU0xEb1Y1dU5UbHAyWmRJTnUxU2FrbWFIQjNXZVN0d0MxbHciLCJlIjoiQVFBQiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMifV19LCJtZXRhZGF0YSI6eyJmZWRlcmF0aW9uX2VudGl0eSI6eyJjb250YWN0cyI6WyJvcHNAbG9jYWxob3N0Il0sImZlZGVyYXRpb25fZmV0Y2hfZW5kcG9pbnQiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvZmV0Y2gvIiwiZmVkZXJhdGlvbl9yZXNvbHZlX2VuZHBvaW50IjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL3Jlc29sdmUvIiwiZmVkZXJhdGlvbl9zdGF0dXNfZW5kcG9pbnQiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvdHJ1c3RfbWFya19zdGF0dXMvIiwiaG9tZXBhZ2VfdXJpIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwibmFtZSI6ImV4YW1wbGUgVEEiLCJmZWRlcmF0aW9uX2xpc3RfZW5kcG9pbnQiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvbGlzdC8ifX0sInRydXN0X21hcmtzX2lzc3VlcnMiOnsiaHR0cHM6Ly93d3cuc3BpZC5nb3YuaXQvY2VydGlmaWNhdGlvbi9ycC9wdWJsaWMiOlsiaHR0cHM6Ly9yZWdpc3RyeS5zcGlkLmFnaWQuZ292Lml0IiwiaHR0cHM6Ly9wdWJsaWMuaW50ZXJtZWRpYXJ5LnNwaWQuaXQiXSwiaHR0cHM6Ly93d3cuc3BpZC5nb3YuaXQvY2VydGlmaWNhdGlvbi9ycC9wcml2YXRlIjpbImh0dHBzOi8vcmVnaXN0cnkuc3BpZC5hZ2lkLmdvdi5pdCIsImh0dHBzOi8vcHJpdmF0ZS5vdGhlci5pbnRlcm1lZGlhcnkuaXQiXSwiaHR0cHM6Ly9zZ2QuYWEuaXQvb25ib2FyZGluZyI6WyJodHRwczovL3NnZC5hYS5pdCJdfSwiY29uc3RyYWludHMiOnsibWF4X3BhdGhfbGVuZ3RoIjoxfX0.YBz4YUjIS-zMLqWAr_d7QKQZ3LGpr87H230J9otzt5gRsl18Uw9kHwOQil3P7nuaNjVSNmgbl6xtOL4XIFjNU0bBcVtxhwxIUkst_31mBMlslxlRrPhNljgL5kj2LnCRlmUOvvfywaWS3WGq3TBmClWUJa0YXuUb-rdvvdlW1Hcsvnriym8Gct63v9JOBVXvfuQFiKiSWYtRlfIqV2g4y_MaW539qxVmghLQaRbBHTDecyzhTUY9gkaPLhfk3cu5DUUvGTDbpMCZnVu7HvTCdLvxLIZjIl7mCDl2NT5aZHPIaf8FyOHJsYl3mtr68PIsDZaBR9_KdnPiTsPlSm3Y7A"
+  }
+},
+{
+  "model": "spid_cie_oidc_entity.fetchedentitystatement",
+  "pk": 13,
+  "fields": {
+    "created": "2022-03-18T08:40:57.521Z",
+    "modified": "2022-03-18T08:40:57.521Z",
+    "iss": "http://127.0.0.1:8000/oidc/rp/",
+    "sub": "http://127.0.0.1:8000/oidc/rp/",
+    "exp": "2022-03-23T13:02:13Z",
+    "iat": "2022-03-21T13:02:13Z",
+    "statement": {
+      "exp": 1648040533,
+      "iat": 1647867733,
+      "iss": "http://127.0.0.1:8000/oidc/rp/",
+      "sub": "http://127.0.0.1:8000/oidc/rp/",
+      "jwks": {
+        "keys": [
+          {
+            "kty": "RSA",
+            "n": "5s4qi1Ta-sEuKb5rJ8TzHmyGKaSu89pIXIi6w4Ekx6GL56mJDNE_MWJHsFjWXajfMdMQmZrSXAvLtXxmbhUui9Mq_IormhmEyyEJS0SyE9UKTxWzi0yd_n_C7OjFBhM-0ZyUlgl81E_sr-35P1A6b5WSYwMvRSR-P9yx_NI-XBQ48G_zdmk3CbuuzZsXZqqgj5U7OGWH-4Huosn9nH3FVkwX0OlWkgWM-J9DEWzGBjl9hfbbrMtM_obljHL2NfT6RJYER2IpdI8RCyQS3sMPt6ZHDskmuNlyMDNATCChXQJLnltwEjxcgvzjw_G9J25DwfdfVEhDF_0kCp44UMmS3Q",
+            "e": "AQAB",
+            "kid": "2HnoFS3YnC9tjiCaivhWLVUJ3AxwGGz_98uRFaqMEEs"
+          }
+        ]
+      },
+      "metadata": {
+        "openid_relying_party": {
+          "application_type": "web",
+          "client_id": "http://127.0.0.1:8000/oidc/rp/",
+          "client_registration_types": [
+            "automatic"
+          ],
+          "jwks": {
+            "keys": [
+              {
+                "kty": "RSA",
+                "use": "sig",
+                "n": "5s4qi1Ta-sEuKb5rJ8TzHmyGKaSu89pIXIi6w4Ekx6GL56mJDNE_MWJHsFjWXajfMdMQmZrSXAvLtXxmbhUui9Mq_IormhmEyyEJS0SyE9UKTxWzi0yd_n_C7OjFBhM-0ZyUlgl81E_sr-35P1A6b5WSYwMvRSR-P9yx_NI-XBQ48G_zdmk3CbuuzZsXZqqgj5U7OGWH-4Huosn9nH3FVkwX0OlWkgWM-J9DEWzGBjl9hfbbrMtM_obljHL2NfT6RJYER2IpdI8RCyQS3sMPt6ZHDskmuNlyMDNATCChXQJLnltwEjxcgvzjw_G9J25DwfdfVEhDF_0kCp44UMmS3Q",
+                "e": "AQAB",
+                "kid": "2HnoFS3YnC9tjiCaivhWLVUJ3AxwGGz_98uRFaqMEEs"
+              }
+            ]
+          },
+          "client_name": "Name of an example organization",
+          "contacts": [
+            "ops@rp.example.it"
+          ],
+          "grant_types": [
+            "refresh_token",
+            "authorization_code"
+          ],
+          "redirect_uris": [
+            "http://127.0.0.1:8000/oidc/rp/callback"
+          ],
+          "response_types": [
+            "code"
+          ],
+          "subject_type": "pairwise"
+        }
+      },
+      "trust_marks": [
+        {
+          "id": "https://www.spid.gov.it/openid-federation/agreement/sp-public/",
+          "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMifQ.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ1NjEyNDAxLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAiLCJtYXJrIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.mSPNR0AOPBn3UNJAIbrWUMQ8vGTetQajpa3i59JDKDXYWqo2TUGh4AQBghCiG3qqV9cl-hleLtuwoeZ1InKHeslTLftVdcR3meeMLs3mLobHYr26Mi7pC7-jx1ZFVyk4GXl7mn9WVSQGEUOiuhL01tdlUfxf0TJSFSOMEZGpCA3hXroLOnEl3FjkAw7sPvjfImsbadbHVusb72HTTs1n5Xo7z3As3fDWHcxD-fvvq0beu9cx-L2sT4YaNC-ELd1M3m5r0NIjjEUAt4Gnot-l5Z3-C_bA41uvh2hX34U_fGZ6jpmuluJo1Lqi26N8LTB-Rbu0UMaZnkRg9E72_YRZig"
+        }
+      ],
+      "authority_hints": []
+    },
+    "jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJIbm9GUzNZbkM5dGppQ2FpdmhXTFZVSjNBeHdHR3pfOTh1UkZhcU1FRXMiLCJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCJ9.eyJleHAiOjE2NDgwNDA1MzMsImlhdCI6MTY0Nzg2NzczMywiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiandrcyI6eyJrZXlzIjpbeyJrdHkiOiJSU0EiLCJuIjoiNXM0cWkxVGEtc0V1S2I1cko4VHpIbXlHS2FTdTg5cElYSWk2dzRFa3g2R0w1Nm1KRE5FX01XSkhzRmpXWGFqZk1kTVFtWnJTWEF2THRYeG1iaFV1aTlNcV9Jb3JtaG1FeXlFSlMwU3lFOVVLVHhXemkweWRfbl9DN09qRkJoTS0wWnlVbGdsODFFX3NyLTM1UDFBNmI1V1NZd012UlNSLVA5eXhfTkktWEJRNDhHX3pkbWszQ2J1dXpac1hacXFnajVVN09HV0gtNEh1b3NuOW5IM0ZWa3dYME9sV2tnV00tSjlERVd6R0JqbDloZmJick10TV9vYmxqSEwyTmZUNlJKWUVSMklwZEk4UkN5UVMzc01QdDZaSERza211Tmx5TUROQVRDQ2hYUUpMbmx0d0VqeGNndnpqd19HOUoyNUR3ZmRmVkVoREZfMGtDcDQ0VU1tUzNRIiwiZSI6IkFRQUIiLCJraWQiOiIySG5vRlMzWW5DOXRqaUNhaXZoV0xWVUozQXh3R0d6Xzk4dVJGYXFNRUVzIn1dfSwibWV0YWRhdGEiOnsib3BlbmlkX3JlbHlpbmdfcGFydHkiOnsiYXBwbGljYXRpb25fdHlwZSI6IndlYiIsImNsaWVudF9pZCI6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMC9vaWRjL3JwLyIsImNsaWVudF9yZWdpc3RyYXRpb25fdHlwZXMiOlsiYXV0b21hdGljIl0sImp3a3MiOnsia2V5cyI6W3sia3R5IjoiUlNBIiwidXNlIjoic2lnIiwibiI6IjVzNHFpMVRhLXNFdUtiNXJKOFR6SG15R0thU3U4OXBJWElpNnc0RWt4NkdMNTZtSkRORV9NV0pIc0ZqV1hhamZNZE1RbVpyU1hBdkx0WHhtYmhVdWk5TXFfSW9ybWhtRXl5RUpTMFN5RTlVS1R4V3ppMHlkX25fQzdPakZCaE0tMFp5VWxnbDgxRV9zci0zNVAxQTZiNVdTWXdNdlJTUi1QOXl4X05JLVhCUTQ4R196ZG1rM0NidXV6WnNYWnFxZ2o1VTdPR1dILTRIdW9zbjluSDNGVmt3WDBPbFdrZ1dNLUo5REVXekdCamw5aGZiYnJNdE1fb2JsakhMMk5mVDZSSllFUjJJcGRJOFJDeVFTM3NNUHQ2WkhEc2ttdU5seU1ETkFUQ0NoWFFKTG5sdHdFanhjZ3Z6andfRzlKMjVEd2ZkZlZFaERGXzBrQ3A0NFVNbVMzUSIsImUiOiJBUUFCIiwia2lkIjoiMkhub0ZTM1luQzl0amlDYWl2aFdMVlVKM0F4d0dHel85OHVSRmFxTUVFcyJ9XX0sImNsaWVudF9uYW1lIjoiTmFtZSBvZiBhbiBleGFtcGxlIG9yZ2FuaXphdGlvbiIsImNvbnRhY3RzIjpbIm9wc0BycC5leGFtcGxlLml0Il0sImdyYW50X3R5cGVzIjpbInJlZnJlc2hfdG9rZW4iLCJhdXRob3JpemF0aW9uX2NvZGUiXSwicmVkaXJlY3RfdXJpcyI6WyJodHRwOi8vMTI3LjAuMC4xOjgwMDAvb2lkYy9ycC9jYWxsYmFjayJdLCJyZXNwb25zZV90eXBlcyI6WyJjb2RlIl0sInN1YmplY3RfdHlwZSI6InBhaXJ3aXNlIn19LCJ0cnVzdF9tYXJrcyI6W3siaWQiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9vcGVuaWQtZmVkZXJhdGlvbi9hZ3JlZW1lbnQvc3AtcHVibGljLyIsInRydXN0X21hcmsiOiJleUpoYkdjaU9pSlNVekkxTmlJc0ltdHBaQ0k2SWtacFpsbDRNRE5pYm05elJEaHRObWRaVVVsbVRraE9VRGxqVFY5VFlXMDVWR00xYmt4c2IwbEpjbU1pZlEuZXlKcGMzTWlPaUpvZEhSd09pOHZNVEkzTGpBdU1DNHhPamd3TURBdklpd2ljM1ZpSWpvaWFIUjBjRG92THpFeU55NHdMakF1TVRvNE1EQXdMMjlwWkdNdmNuQXZJaXdpYVdGMElqb3hOalExTmpFeU5EQXhMQ0pwWkNJNkltaDBkSEJ6T2k4dmQzZDNMbk53YVdRdVoyOTJMbWwwTDJObGNuUnBabWxqWVhScGIyNHZjbkFpTENKdFlYSnJJam9pYUhSMGNITTZMeTkzZDNjdVlXZHBaQzVuYjNZdWFYUXZkR2hsYldWekwyTjFjM1J2YlM5aFoybGtMMnh2WjI4dWMzWm5JaXdpY21WbUlqb2lhSFIwY0hNNkx5OWtiMk56TG1sMFlXeHBZUzVwZEM5cGRHRnNhV0V2YzNCcFpDOXpjR2xrTFhKbFoyOXNaUzEwWldOdWFXTm9aUzF2YVdSakwybDBMM04wWVdKcGJHVXZhVzVrWlhndWFIUnRiQ0o5Lm1TUE5SMEFPUEJuM1VOSkFJYnJXVU1ROHZHVGV0UWFqcGEzaTU5SkRLRFhZV3FvMlRVR2g0QVFCZ2hDaUczcXFWOWNsLWhsZUx0dXdvZVoxSW5LSGVzbFRMZnRWZGNSM21lZU1MczNtTG9iSFlyMjZNaTdwQzctangxWkZWeWs0R1hsN21uOVdWU1FHRVVPaXVoTDAxdGRsVWZ4ZjBUSlNGU09NRVpHcENBM2hYcm9MT25FbDNGamtBdzdzUHZqZkltc2JhZGJIVnVzYjcySFRUczFuNVhvN3ozQXMzZkRXSGN4RC1mdnZxMGJldTljeC1MMnNUNFlhTkMtRUxkMU0zbTVyME5JampFVUF0NEdub3QtbDVaMy1DX2JBNDF1dmgyaFgzNFVfZkdaNmpwbXVsdUpvMUxxaTI2TjhMVEItUmJ1MFVNYVpua1JnOUU3Ml9ZUlppZyJ9XSwiYXV0aG9yaXR5X2hpbnRzIjpbImh0dHA6Ly8xMjcuMC4wLjE6ODAwMC8iXX0.Hv3RZ2Jz29o6r4QkwLB2JMxHz_NFLDQQ5WL4sjSILYTbIOpA-ufW2DIYsyruAfRYLhGSe2GhP5Tf5f8dmU8NhGHG7XkXfnvZY_mZ21TvGeO6WhFskfDEYrEkBqQfgNcHdGZrbLuN40oX3PhnRjpaq_XnF7RhM1hRjj69bIlISWEV9NJpg1CSBD3QQAJgKOiGRdXReHEoQsRdsJj2bvyUfHLbH3qYDY_rtJgBJGLKEvX-JDRoVeFI-oE5LqneArhll_3aLN_5G14UXW3HQkoqw9y1hB_JTs58mRsdEPlcW_X6KN1Ym3vQMStWQtu79cw6X2JzeY38RqEZ1E69YX5ngA"
+  }
+},
+{
+  "model": "spid_cie_oidc_entity.fetchedentitystatement",
+  "pk": 14,
+  "fields": {
+    "created": "2022-03-18T08:40:57.534Z",
+    "modified": "2022-03-18T08:40:57.534Z",
+    "iss": "http://127.0.0.1:8000/",
+    "sub": "http://127.0.0.1:8000/oidc/rp/",
+    "exp": "2022-03-23T13:02:13Z",
+    "iat": "2022-03-21T13:02:13Z",
+    "statement": {
+      "exp": 1648040533,
+      "iat": 1647867733,
+      "iss": "http://127.0.0.1:8000/",
+      "sub": "http://127.0.0.1:8000/oidc/rp/",
+      "jwks": {
+        "keys": [
+          {
+            "kty": "RSA",
+            "n": "5s4qi1Ta-sEuKb5rJ8TzHmyGKaSu89pIXIi6w4Ekx6GL56mJDNE_MWJHsFjWXajfMdMQmZrSXAvLtXxmbhUui9Mq_IormhmEyyEJS0SyE9UKTxWzi0yd_n_C7OjFBhM-0ZyUlgl81E_sr-35P1A6b5WSYwMvRSR-P9yx_NI-XBQ48G_zdmk3CbuuzZsXZqqgj5U7OGWH-4Huosn9nH3FVkwX0OlWkgWM-J9DEWzGBjl9hfbbrMtM_obljHL2NfT6RJYER2IpdI8RCyQS3sMPt6ZHDskmuNlyMDNATCChXQJLnltwEjxcgvzjw_G9J25DwfdfVEhDF_0kCp44UMmS3Q",
+            "e": "AQAB",
+            "kid": "2HnoFS3YnC9tjiCaivhWLVUJ3AxwGGz_98uRFaqMEEs"
+          }
+        ]
+      },
+      "metadata_policy": {
+        "openid_relying_party": {
+          "scopes": {
+            "superset_of": [
+              "openid"
+            ],
+            "subset_of": [
+              "openid",
+              "offline_access",
+              "profile",
+              "email"
+            ]
+          },
+          "contacts": {
+            "add": [
+              "ciao@email.it"
+            ]
+          }
+        }
+      },
+      "trust_marks": [
+        {
+          "id": "https://www.spid.gov.it/openid-federation/agreement/sp-public/",
+          "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ3ODY3NzMzLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAiLCJtYXJrIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.aChWzvPMERsSmdGPNrOcYYJeaeVd7v3ilwOh_IsUkXy7JNvpRaKiexaKqPtveIY8r5hkDK89BCEt16AbXjfUfvXZSbJdeTTnzWUXqDdXHKWZ1010gKLmKMqRuNxeJZzgke4JniuoiXQg0g5AHyXgxqngTfeFRDziegC74NZmeo3IqIR714aIg6-FERPTFqXA2-FgYKTzYvzIZHnWXuW6Iwva2y3w07sD6uqGHMv3_CNtvU_QBLETOJbxy6q04Eq-jYmW1G_JMaB4XKAdYsMXU0jiwSlwLry2-Fc-nAv7wokldEz-0caRgEe5zW0czo-QWCoe-d_QENk8vo9-QzB-Zg"
+        },
+        {
+          "id": "https://www.spid.gov.it/openid-federation/agreement/sp-private",
+          "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ3ODY3NzMzLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAvcHJpdmF0ZSIsImxvZ29fdXJpIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.WBZ353cr3kWmQZSLC1XLWYf6eYB5DrbyYPgd7GBYM4vT3LzBYDy3ZsK_E2FWZ4VD19juNJ-qtOPbdZ2YHZBgyqyJ2HKLDTlW2Upszdk16RQ5BFbW0y3NqBp1-PZHbFrG-RmHnKcNFxVP9NmD2KGw_LxATMeB17S76GaaHK-mt2yVAGU5RxqF7gvxtPvMM7EYuw03inAPv_SOwYglM1TMLNgt-lvAPbBXVfbUEDyB6qZWm_1AsGh3WQqqaOf8znfT5t66BpGtnQT2cFCvbN-WiAKzkUzDOurdFN7YuLsNt1Mz0U5Y7ddZb6WvB1g-t98x03q8JNEXRKNkY2tH_sCFZA"
+        }
+      ]
+    },
+    "jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCJ9.eyJleHAiOjE2NDgwNDA1MzMsImlhdCI6MTY0Nzg2NzczMywiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwLyIsInN1YiI6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMC9vaWRjL3JwLyIsImp3a3MiOnsia2V5cyI6W3sia3R5IjoiUlNBIiwibiI6IjVzNHFpMVRhLXNFdUtiNXJKOFR6SG15R0thU3U4OXBJWElpNnc0RWt4NkdMNTZtSkRORV9NV0pIc0ZqV1hhamZNZE1RbVpyU1hBdkx0WHhtYmhVdWk5TXFfSW9ybWhtRXl5RUpTMFN5RTlVS1R4V3ppMHlkX25fQzdPakZCaE0tMFp5VWxnbDgxRV9zci0zNVAxQTZiNVdTWXdNdlJTUi1QOXl4X05JLVhCUTQ4R196ZG1rM0NidXV6WnNYWnFxZ2o1VTdPR1dILTRIdW9zbjluSDNGVmt3WDBPbFdrZ1dNLUo5REVXekdCamw5aGZiYnJNdE1fb2JsakhMMk5mVDZSSllFUjJJcGRJOFJDeVFTM3NNUHQ2WkhEc2ttdU5seU1ETkFUQ0NoWFFKTG5sdHdFanhjZ3Z6andfRzlKMjVEd2ZkZlZFaERGXzBrQ3A0NFVNbVMzUSIsImUiOiJBUUFCIiwia2lkIjoiMkhub0ZTM1luQzl0amlDYWl2aFdMVlVKM0F4d0dHel85OHVSRmFxTUVFcyJ9XX0sIm1ldGFkYXRhX3BvbGljeSI6eyJvcGVuaWRfcmVseWluZ19wYXJ0eSI6eyJzY29wZXMiOnsic3VwZXJzZXRfb2YiOlsib3BlbmlkIl0sInN1YnNldF9vZiI6WyJvcGVuaWQiLCJvZmZsaW5lX2FjY2VzcyIsInByb2ZpbGUiLCJlbWFpbCJdfSwiY29udGFjdHMiOnsiYWRkIjpbImNpYW9AZW1haWwuaXQiXX19fSwidHJ1c3RfbWFya3MiOlt7ImlkIjoiaHR0cHM6Ly93d3cuc3BpZC5nb3YuaXQvb3BlbmlkLWZlZGVyYXRpb24vYWdyZWVtZW50L3NwLXB1YmxpYy8iLCJ0cnVzdF9tYXJrIjoiZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklrWnBabGw0TUROaWJtOXpSRGh0Tm1kWlVVbG1Ua2hPVURsalRWOVRZVzA1VkdNMWJreHNiMGxKY21NaUxDSjBlWEFpT2lKMGNuVnpkQzF0WVhKcksycDNkQ0o5LmV5SnBjM01pT2lKb2RIUndPaTh2TVRJM0xqQXVNQzR4T2pnd01EQXZJaXdpYzNWaUlqb2lhSFIwY0Rvdkx6RXlOeTR3TGpBdU1UbzRNREF3TDI5cFpHTXZjbkF2SWl3aWFXRjBJam94TmpRM09EWTNOek16TENKcFpDSTZJbWgwZEhCek9pOHZkM2QzTG5Od2FXUXVaMjkyTG1sMEwyTmxjblJwWm1sallYUnBiMjR2Y25BaUxDSnRZWEpySWpvaWFIUjBjSE02THk5M2QzY3VZV2RwWkM1bmIzWXVhWFF2ZEdobGJXVnpMMk4xYzNSdmJTOWhaMmxrTDJ4dloyOHVjM1puSWl3aWNtVm1Jam9pYUhSMGNITTZMeTlrYjJOekxtbDBZV3hwWVM1cGRDOXBkR0ZzYVdFdmMzQnBaQzl6Y0dsa0xYSmxaMjlzWlMxMFpXTnVhV05vWlMxdmFXUmpMMmwwTDNOMFlXSnBiR1V2YVc1a1pYZ3VhSFJ0YkNKOS5hQ2hXenZQTUVSc1NtZEdQTnJPY1lZSmVhZVZkN3YzaWx3T2hfSXNVa1h5N0pOdnBSYUtpZXhhS3FQdHZlSVk4cjVoa0RLODlCQ0V0MTZBYlhqZlVmdlhaU2JKZGVUVG56V1VYcURkWEhLV1oxMDEwZ0tMbUtNcVJ1TnhlSlp6Z2tlNEpuaXVvaVhRZzBnNUFIeVhneHFuZ1RmZUZSRHppZWdDNzROWm1lbzNJcUlSNzE0YUlnNi1GRVJQVEZxWEEyLUZnWUtUell2eklaSG5XWHVXNkl3dmEyeTN3MDdzRDZ1cUdITXYzX0NOdHZVX1FCTEVUT0pieHk2cTA0RXEtalltVzFHX0pNYUI0WEtBZFlzTVhVMGppd1Nsd0xyeTItRmMtbkF2N3dva2xkRXotMGNhUmdFZTV6VzBjem8tUVdDb2UtZF9RRU5rOHZvOS1RekItWmcifSx7ImlkIjoiaHR0cHM6Ly93d3cuc3BpZC5nb3YuaXQvb3BlbmlkLWZlZGVyYXRpb24vYWdyZWVtZW50L3NwLXByaXZhdGUiLCJ0cnVzdF9tYXJrIjoiZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklrWnBabGw0TUROaWJtOXpSRGh0Tm1kWlVVbG1Ua2hPVURsalRWOVRZVzA1VkdNMWJreHNiMGxKY21NaUxDSjBlWEFpT2lKMGNuVnpkQzF0WVhKcksycDNkQ0o5LmV5SnBjM01pT2lKb2RIUndPaTh2TVRJM0xqQXVNQzR4T2pnd01EQXZJaXdpYzNWaUlqb2lhSFIwY0Rvdkx6RXlOeTR3TGpBdU1UbzRNREF3TDI5cFpHTXZjbkF2SWl3aWFXRjBJam94TmpRM09EWTNOek16TENKcFpDSTZJbWgwZEhCek9pOHZkM2QzTG5Od2FXUXVaMjkyTG1sMEwyTmxjblJwWm1sallYUnBiMjR2Y25BdmNISnBkbUYwWlNJc0lteHZaMjlmZFhKcElqb2lhSFIwY0hNNkx5OTNkM2N1WVdkcFpDNW5iM1l1YVhRdmRHaGxiV1Z6TDJOMWMzUnZiUzloWjJsa0wyeHZaMjh1YzNabklpd2ljbVZtSWpvaWFIUjBjSE02THk5a2IyTnpMbWwwWVd4cFlTNXBkQzlwZEdGc2FXRXZjM0JwWkM5emNHbGtMWEpsWjI5c1pTMTBaV051YVdOb1pTMXZhV1JqTDJsMEwzTjBZV0pwYkdVdmFXNWtaWGd1YUhSdGJDSjkuV0JaMzUzY3Iza1dtUVpTTEMxWExXWWY2ZVlCNURyYnlZUGdkN0dCWU00dlQzTHpCWUR5M1pzS19FMkZXWjRWRDE5anVOSi1xdE9QYmRaMllIWkJneXF5SjJIS0xEVGxXMlVwc3pkazE2UlE1QkZiVzB5M05xQnAxLVBaSGJGckctUm1IbktjTkZ4VlA5Tm1EMktHd19MeEFUTWVCMTdTNzZHYWFISy1tdDJ5VkFHVTVSeHFGN2d2eHRQdk1NN0VZdXcwM2luQVB2X1NPd1lnbE0xVE1MTmd0LWx2QVBiQlhWZmJVRUR5QjZxWldtXzFBc0doM1dRcXFhT2Y4em5mVDV0NjZCcEd0blFUMmNGQ3ZiTi1XaUFLemtVekRPdXJkRk43WXVMc050MU16MFU1WTdkZFpiNld2QjFnLXQ5OHgwM3E4Sk5FWFJLTmtZMnRIX3NDRlpBIn1dfQ.oXEyDw_ch0Ximu35_pmFCTtgKEgEhT6ebaXBuy1hqZmZGQr7P2XJtMx1ZCVuQKz79jcEMA0JD4Nzvcu99X-qeqaYOcWkXG2WuM0x8YkdGo-Ghqqxp7HFr9xgSnGtx7Y0RJNKpI79v0PAAoUZxuQHLL1dVtcnqbDpn53q2cZ7xnuaFyh99Ibio21yidqnr76XtesmiLRlSiOaKmHGttSVrkk1qO-sgRLeb-VzeRcRvtOtA3jBvxr2xT8jzpO6M-9Oew-pV7IACKE7jjHLWCzmnmzO90LPJmA7SRxqlbQk7qIVl5nWim48tA4V9PoObp5hs9Ahb9zvdKsSkOKPzwhNaQ"
+  }
+},
+{
+  "model": "spid_cie_oidc_entity.fetchedentitystatement",
+  "pk": 15,
+  "fields": {
+    "created": "2022-03-18T11:59:36.536Z",
+    "modified": "2022-03-18T11:59:36.536Z",
+    "iss": "http://127.0.0.1:8001/",
+    "sub": "http://127.0.0.1:8001/",
+    "exp": "2022-03-23T10:28:22Z",
+    "iat": "2022-03-21T10:28:22Z",
+    "statement": {
+      "exp": 1648031302,
+      "iat": 1647858502,
+      "iss": "http://127.0.0.1:8001/",
+      "sub": "http://127.0.0.1:8001/",
+      "jwks": {
+        "keys": [
+          {
+            "kty": "RSA",
+            "e": "AQAB",
+            "n": "uXfJA-wTlTCA4FdsoE0qZfmKIgedmarrtWgQbElKbWg9RDR7Z8JVBaRLFqwyfyG1JJFm64G51cBJwLIFwWoF7nxsH9VYLm5ocjAnsR4RhlfVE0y_60wjf8skJgBRpiXQPlwH9jDGaqVE_PEBTObDO5w3XourD1F360-v5cLDLRHdFJIitdEVtqATqY5DglRDaKiBhis7a5_1bk839PDLaQhju4XJk4tvDy5-LVkMy5sP2zU6-1tJdA-VmaBZLXy9n0967FGIWmMzpafrBMOuHFcUOH56o-clDah_CITH1dq2D64K0MYhEpACO2p8AH4K8Q6YuJ1dnkVDDwZp2C84sQ",
+            "kid": "YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs"
+          }
+        ]
+      },
+      "metadata": {
+        "openid_relying_party": {
+          "application_type": "web",
+          "client_id": "http://127.0.0.1:8001/",
+          "client_registration_types": [
+            "automatic"
+          ],
+          "jwks": {
+            "keys": [
+              {
+                "kty": "RSA",
+                "use": "sig",
+                "n": "uXfJA-wTlTCA4FdsoE0qZfmKIgedmarrtWgQbElKbWg9RDR7Z8JVBaRLFqwyfyG1JJFm64G51cBJwLIFwWoF7nxsH9VYLm5ocjAnsR4RhlfVE0y_60wjf8skJgBRpiXQPlwH9jDGaqVE_PEBTObDO5w3XourD1F360-v5cLDLRHdFJIitdEVtqATqY5DglRDaKiBhis7a5_1bk839PDLaQhju4XJk4tvDy5-LVkMy5sP2zU6-1tJdA-VmaBZLXy9n0967FGIWmMzpafrBMOuHFcUOH56o-clDah_CITH1dq2D64K0MYhEpACO2p8AH4K8Q6YuJ1dnkVDDwZp2C84sQ",
+                "e": "AQAB",
+                "kid": "YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs"
+              }
+            ]
+          },
+          "client_name": "Name of an example organization",
+          "contacts": [
+            "ops@rp.example.it"
+          ],
+          "grant_types": [
+            "refresh_token",
+            "authorization_code"
+          ],
+          "redirect_uris": [
+            "http://127.0.0.1:8001/oidc/rp/callback"
+          ],
+          "response_types": [
+            "code"
+          ],
+          "subject_type": "pairwise"
+        }
+      },
+      "trust_marks": [
+        {
+          "id": "https://www.spid.gov.it/openid-federation/agreement/sp-public/",
+          "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMifQ.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ1NjEyNDAxLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAiLCJtYXJrIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.mSPNR0AOPBn3UNJAIbrWUMQ8vGTetQajpa3i59JDKDXYWqo2TUGh4AQBghCiG3qqV9cl-hleLtuwoeZ1InKHeslTLftVdcR3meeMLs3mLobHYr26Mi7pC7-jx1ZFVyk4GXl7mn9WVSQGEUOiuhL01tdlUfxf0TJSFSOMEZGpCA3hXroLOnEl3FjkAw7sPvjfImsbadbHVusb72HTTs1n5Xo7z3As3fDWHcxD-fvvq0beu9cx-L2sT4YaNC-ELd1M3m5r0NIjjEUAt4Gnot-l5Z3-C_bA41uvh2hX34U_fGZ6jpmuluJo1Lqi26N8LTB-Rbu0UMaZnkRg9E72_YRZig"
+        }
+      ],
+      "authority_hints": []
+    },
+    "jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6IllodUlKVTZvMTVFVUN5cUEwTEhFcUpkLXhWUEpnb3lXNXdaMW80cGFkV3MiLCJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCJ9.eyJleHAiOjE2NDgwMzEzMDIsImlhdCI6MTY0Nzg1ODUwMiwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAxLyIsInN1YiI6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMS8iLCJqd2tzIjp7ImtleXMiOlt7Imt0eSI6IlJTQSIsImUiOiJBUUFCIiwibiI6InVYZkpBLXdUbFRDQTRGZHNvRTBxWmZtS0lnZWRtYXJydFdnUWJFbEtiV2c5UkRSN1o4SlZCYVJMRnF3eWZ5RzFKSkZtNjRHNTFjQkp3TElGd1dvRjdueHNIOVZZTG01b2NqQW5zUjRSaGxmVkUweV82MHdqZjhza0pnQlJwaVhRUGx3SDlqREdhcVZFX1BFQlRPYkRPNXczWG91ckQxRjM2MC12NWNMRExSSGRGSklpdGRFVnRxQVRxWTVEZ2xSRGFLaUJoaXM3YTVfMWJrODM5UERMYVFoanU0WEprNHR2RHk1LUxWa015NXNQMnpVNi0xdEpkQS1WbWFCWkxYeTluMDk2N0ZHSVdtTXpwYWZyQk1PdUhGY1VPSDU2by1jbERhaF9DSVRIMWRxMkQ2NEswTVloRXBBQ08ycDhBSDRLOFE2WXVKMWRua1ZERHdacDJDODRzUSIsImtpZCI6IllodUlKVTZvMTVFVUN5cUEwTEhFcUpkLXhWUEpnb3lXNXdaMW80cGFkV3MifV19LCJtZXRhZGF0YSI6eyJvcGVuaWRfcmVseWluZ19wYXJ0eSI6eyJhcHBsaWNhdGlvbl90eXBlIjoid2ViIiwiY2xpZW50X2lkIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAxLyIsImNsaWVudF9yZWdpc3RyYXRpb25fdHlwZXMiOlsiYXV0b21hdGljIl0sImp3a3MiOnsia2V5cyI6W3sia3R5IjoiUlNBIiwidXNlIjoic2lnIiwibiI6InVYZkpBLXdUbFRDQTRGZHNvRTBxWmZtS0lnZWRtYXJydFdnUWJFbEtiV2c5UkRSN1o4SlZCYVJMRnF3eWZ5RzFKSkZtNjRHNTFjQkp3TElGd1dvRjdueHNIOVZZTG01b2NqQW5zUjRSaGxmVkUweV82MHdqZjhza0pnQlJwaVhRUGx3SDlqREdhcVZFX1BFQlRPYkRPNXczWG91ckQxRjM2MC12NWNMRExSSGRGSklpdGRFVnRxQVRxWTVEZ2xSRGFLaUJoaXM3YTVfMWJrODM5UERMYVFoanU0WEprNHR2RHk1LUxWa015NXNQMnpVNi0xdEpkQS1WbWFCWkxYeTluMDk2N0ZHSVdtTXpwYWZyQk1PdUhGY1VPSDU2by1jbERhaF9DSVRIMWRxMkQ2NEswTVloRXBBQ08ycDhBSDRLOFE2WXVKMWRua1ZERHdacDJDODRzUSIsImUiOiJBUUFCIiwia2lkIjoiWWh1SUpVNm8xNUVVQ3lxQTBMSEVxSmQteFZQSmdveVc1d1oxbzRwYWRXcyJ9XX0sImNsaWVudF9uYW1lIjoiTmFtZSBvZiBhbiBleGFtcGxlIG9yZ2FuaXphdGlvbiIsImNvbnRhY3RzIjpbIm9wc0BycC5leGFtcGxlLml0Il0sImdyYW50X3R5cGVzIjpbInJlZnJlc2hfdG9rZW4iLCJhdXRob3JpemF0aW9uX2NvZGUiXSwicmVkaXJlY3RfdXJpcyI6WyJodHRwOi8vMTI3LjAuMC4xOjgwMDEvb2lkYy9ycC9jYWxsYmFjayJdLCJyZXNwb25zZV90eXBlcyI6WyJjb2RlIl0sInN1YmplY3RfdHlwZSI6InBhaXJ3aXNlIn19LCJ0cnVzdF9tYXJrcyI6W3siaWQiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9vcGVuaWQtZmVkZXJhdGlvbi9hZ3JlZW1lbnQvc3AtcHVibGljLyIsInRydXN0X21hcmsiOiJleUpoYkdjaU9pSlNVekkxTmlJc0ltdHBaQ0k2SWtacFpsbDRNRE5pYm05elJEaHRObWRaVVVsbVRraE9VRGxqVFY5VFlXMDVWR00xYmt4c2IwbEpjbU1pZlEuZXlKcGMzTWlPaUpvZEhSd09pOHZNVEkzTGpBdU1DNHhPamd3TURBdklpd2ljM1ZpSWpvaWFIUjBjRG92THpFeU55NHdMakF1TVRvNE1EQXdMMjlwWkdNdmNuQXZJaXdpYVdGMElqb3hOalExTmpFeU5EQXhMQ0pwWkNJNkltaDBkSEJ6T2k4dmQzZDNMbk53YVdRdVoyOTJMbWwwTDJObGNuUnBabWxqWVhScGIyNHZjbkFpTENKdFlYSnJJam9pYUhSMGNITTZMeTkzZDNjdVlXZHBaQzVuYjNZdWFYUXZkR2hsYldWekwyTjFjM1J2YlM5aFoybGtMMnh2WjI4dWMzWm5JaXdpY21WbUlqb2lhSFIwY0hNNkx5OWtiMk56TG1sMFlXeHBZUzVwZEM5cGRHRnNhV0V2YzNCcFpDOXpjR2xrTFhKbFoyOXNaUzEwWldOdWFXTm9aUzF2YVdSakwybDBMM04wWVdKcGJHVXZhVzVrWlhndWFIUnRiQ0o5Lm1TUE5SMEFPUEJuM1VOSkFJYnJXVU1ROHZHVGV0UWFqcGEzaTU5SkRLRFhZV3FvMlRVR2g0QVFCZ2hDaUczcXFWOWNsLWhsZUx0dXdvZVoxSW5LSGVzbFRMZnRWZGNSM21lZU1MczNtTG9iSFlyMjZNaTdwQzctangxWkZWeWs0R1hsN21uOVdWU1FHRVVPaXVoTDAxdGRsVWZ4ZjBUSlNGU09NRVpHcENBM2hYcm9MT25FbDNGamtBdzdzUHZqZkltc2JhZGJIVnVzYjcySFRUczFuNVhvN3ozQXMzZkRXSGN4RC1mdnZxMGJldTljeC1MMnNUNFlhTkMtRUxkMU0zbTVyME5JampFVUF0NEdub3QtbDVaMy1DX2JBNDF1dmgyaFgzNFVfZkdaNmpwbXVsdUpvMUxxaTI2TjhMVEItUmJ1MFVNYVpua1JnOUU3Ml9ZUlppZyJ9XSwiYXV0aG9yaXR5X2hpbnRzIjpbImh0dHA6Ly8xMjcuMC4wLjE6ODAwMC8iXX0.X6yaLi4UFn0yveOmQcls5XTmG2W3kgYRDNGQFNJ4JTuj-RNfmSSVP41ifsRVhejgbyLiUuLmDS08ldAJZ0xRvQqVXmVcDb8ZLu1Rn1jRcrRH7AtCJBYmIqP6Mu4lfjfkf2pK6feyp3J4h6g5sxkB3bu9V44SK0yWnyFhGhrkVTslxwSr-bsDl2pwIa659raSkdldcgYmGNiBSAnSLkaTog4cExjgxx6eNUWFXvXBkA73XNMjShNRINE5GVI1svHlsJzL1Bw_KyH-NlGhtRhi0dp-nEJOJxabBTJESLTD1RCmIIXLv69gJY2sKmC5OSLunVqsVOTllgtSOB20AOql8Q"
+  }
+},
+{
+  "model": "spid_cie_oidc_entity.fetchedentitystatement",
+  "pk": 16,
+  "fields": {
+    "created": "2022-03-18T11:59:36.550Z",
+    "modified": "2022-03-18T11:59:36.550Z",
+    "iss": "http://127.0.0.1:8000/",
+    "sub": "http://127.0.0.1:8001/",
+    "exp": "2022-03-23T10:28:22Z",
+    "iat": "2022-03-21T10:28:22Z",
+    "statement": {
+      "exp": 1648031302,
+      "iat": 1647858502,
+      "iss": "http://127.0.0.1:8000/",
+      "sub": "http://127.0.0.1:8001/",
+      "jwks": {
+        "keys": [
+          {
+            "kty": "RSA",
+            "n": "uXfJA-wTlTCA4FdsoE0qZfmKIgedmarrtWgQbElKbWg9RDR7Z8JVBaRLFqwyfyG1JJFm64G51cBJwLIFwWoF7nxsH9VYLm5ocjAnsR4RhlfVE0y_60wjf8skJgBRpiXQPlwH9jDGaqVE_PEBTObDO5w3XourD1F360-v5cLDLRHdFJIitdEVtqATqY5DglRDaKiBhis7a5_1bk839PDLaQhju4XJk4tvDy5-LVkMy5sP2zU6-1tJdA-VmaBZLXy9n0967FGIWmMzpafrBMOuHFcUOH56o-clDah_CITH1dq2D64K0MYhEpACO2p8AH4K8Q6YuJ1dnkVDDwZp2C84sQ",
+            "e": "AQAB",
+            "kid": "YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs"
+          }
+        ]
+      },
+      "metadata_policy": {
+        "openid_relying_party": {
+          "grant_types": {
+            "subset_of": [
+              "authorization_code",
+              "refresh_token"
+            ]
+          },
+          "scopes": {
+            "superset_of": [
+              "openid"
+            ],
+            "subset_of": [
+              "openid",
+              "offline_access"
+            ]
+          }
+        }
+      },
+      "trust_marks": [
+        {
+          "id": "https://www.spid.gov.it/openid-federation/agreement/sp-private",
+          "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAxLyIsImlhdCI6MTY0Nzg1ODUwMiwiaWQiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9jZXJ0aWZpY2F0aW9uL3JwL3ByaXZhdGUiLCJsb2dvX3VyaSI6Imh0dHBzOi8vd3d3LmFnaWQuZ292Lml0L3RoZW1lcy9jdXN0b20vYWdpZC9sb2dvLnN2ZyIsInJlZiI6Imh0dHBzOi8vZG9jcy5pdGFsaWEuaXQvaXRhbGlhL3NwaWQvc3BpZC1yZWdvbGUtdGVjbmljaGUtb2lkYy9pdC9zdGFiaWxlL2luZGV4Lmh0bWwifQ.HJOizHTlrrUrVnILWgXu8-5RC7u5DK2zWMU4ROIfKWEhMDHdoQbtsdx_9Yia6P6f_5wZZs_oavUGeZ8PirFgsDm-zl38YHnR2lBfnGH038jEzhzxPAJmA0D4LaznWzO0ca20GlfWDnLK2z2QT5FzXlXwavAqSHiSfPT6379tANYOnPiIGLhWdVgIlvmbLKxNi310eCxiO_OLVf68pTVZ1QQKlmdNHBvXukGpPUt3tQYuWIk7kDSs6wfURC0yzuGKyzxxXnbl-iHoH7wFbe9bJ1JbmTe_1feuKQFdA6ltJ0TVudCjsp7PfS6Jio7hT35DroNZiRce0_S6aJZEUDTOWg"
+        }
+      ]
+    },
+    "jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCJ9.eyJleHAiOjE2NDgwMzEzMDIsImlhdCI6MTY0Nzg1ODUwMiwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwLyIsInN1YiI6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMS8iLCJqd2tzIjp7ImtleXMiOlt7Imt0eSI6IlJTQSIsIm4iOiJ1WGZKQS13VGxUQ0E0RmRzb0UwcVpmbUtJZ2VkbWFycnRXZ1FiRWxLYldnOVJEUjdaOEpWQmFSTEZxd3lmeUcxSkpGbTY0RzUxY0JKd0xJRndXb0Y3bnhzSDlWWUxtNW9jakFuc1I0UmhsZlZFMHlfNjB3amY4c2tKZ0JScGlYUVBsd0g5akRHYXFWRV9QRUJUT2JETzV3M1hvdXJEMUYzNjAtdjVjTERMUkhkRkpJaXRkRVZ0cUFUcVk1RGdsUkRhS2lCaGlzN2E1XzFiazgzOVBETGFRaGp1NFhKazR0dkR5NS1MVmtNeTVzUDJ6VTYtMXRKZEEtVm1hQlpMWHk5bjA5NjdGR0lXbU16cGFmckJNT3VIRmNVT0g1Nm8tY2xEYWhfQ0lUSDFkcTJENjRLME1ZaEVwQUNPMnA4QUg0SzhRNll1SjFkbmtWRER3WnAyQzg0c1EiLCJlIjoiQVFBQiIsImtpZCI6IllodUlKVTZvMTVFVUN5cUEwTEhFcUpkLXhWUEpnb3lXNXdaMW80cGFkV3MifV19LCJtZXRhZGF0YV9wb2xpY3kiOnsib3BlbmlkX3JlbHlpbmdfcGFydHkiOnsiZ3JhbnRfdHlwZXMiOnsic3Vic2V0X29mIjpbImF1dGhvcml6YXRpb25fY29kZSIsInJlZnJlc2hfdG9rZW4iXX0sInNjb3BlcyI6eyJzdXBlcnNldF9vZiI6WyJvcGVuaWQiXSwic3Vic2V0X29mIjpbIm9wZW5pZCIsIm9mZmxpbmVfYWNjZXNzIl19fX0sInRydXN0X21hcmtzIjpbeyJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L29wZW5pZC1mZWRlcmF0aW9uL2FncmVlbWVudC9zcC1wcml2YXRlIiwidHJ1c3RfbWFyayI6ImV5SmhiR2NpT2lKU1V6STFOaUlzSW10cFpDSTZJa1pwWmxsNE1ETmlibTl6UkRodE5tZFpVVWxtVGtoT1VEbGpUVjlUWVcwNVZHTTFia3hzYjBsSmNtTWlMQ0owZVhBaU9pSjBjblZ6ZEMxdFlYSnJLMnAzZENKOS5leUpwYzNNaU9pSm9kSFJ3T2k4dk1USTNMakF1TUM0eE9qZ3dNREF2SWl3aWMzVmlJam9pYUhSMGNEb3ZMekV5Tnk0d0xqQXVNVG80TURBeEx5SXNJbWxoZENJNk1UWTBOemcxT0RVd01pd2lhV1FpT2lKb2RIUndjem92TDNkM2R5NXpjR2xrTG1kdmRpNXBkQzlqWlhKMGFXWnBZMkYwYVc5dUwzSndMM0J5YVhaaGRHVWlMQ0pzYjJkdlgzVnlhU0k2SW1oMGRIQnpPaTh2ZDNkM0xtRm5hV1F1WjI5MkxtbDBMM1JvWlcxbGN5OWpkWE4wYjIwdllXZHBaQzlzYjJkdkxuTjJaeUlzSW5KbFppSTZJbWgwZEhCek9pOHZaRzlqY3k1cGRHRnNhV0V1YVhRdmFYUmhiR2xoTDNOd2FXUXZjM0JwWkMxeVpXZHZiR1V0ZEdWamJtbGphR1V0YjJsa1l5OXBkQzl6ZEdGaWFXeGxMMmx1WkdWNExtaDBiV3dpZlEuSEpPaXpIVGxyclVyVm5JTFdnWHU4LTVSQzd1NURLMnpXTVU0Uk9JZktXRWhNREhkb1FidHNkeF85WWlhNlA2Zl81d1pac19vYXZVR2VaOFBpckZnc0RtLXpsMzhZSG5SMmxCZm5HSDAzOGpFemh6eFBBSm1BMEQ0TGF6bld6TzBjYTIwR2xmV0RuTEsyejJRVDVGelhsWHdhdkFxU0hpU2ZQVDYzNzl0QU5ZT25QaUlHTGhXZFZnSWx2bWJMS3hOaTMxMGVDeGlPX09MVmY2OHBUVloxUVFLbG1kTkhCdlh1a0dwUFV0M3RRWXVXSWs3a0RTczZ3ZlVSQzB5enVHS3l6eHhYbmJsLWlIb0g3d0ZiZTliSjFKYm1UZV8xZmV1S1FGZEE2bHRKMFRWdWRDanNwN1BmUzZKaW83aFQzNURyb05aaVJjZTBfUzZhSlpFVURUT1dnIn1dfQ.plfLf25SjYxCvpo_b1L5vNo30JmECYnp4fKHcOE7QbbnRjRvBHuWxLNF1Fli5FMAoMwvV_Vyp6rFoQ3BwkMKJvViAB-lz-c-bDxHpZoiCHYnu6sZ8zITPFBylnBKyfW6S707OZPCg8YeIeL_bja_K8Ag0YMNL_MFmWwGhgKSLgYnQLj0BDf_fvjQcjDpz7P60bmwL6f18meX3xRwFS5OUR7JH4B7hl3DJgF4QI4DjvBd1aAOXEy1jQ6Er5Qy7CUZK9CzRHNciJpH3c_SSQeIaqf5A4nklpEzqvrJGmNebAH1oAofyGm25OasIeAyHUO2ObE451Va2qZCdp6pcRRm2w"
+  }
+},
+{
+  "model": "spid_cie_oidc_entity.trustchain",
+  "pk": 8,
+  "fields": {
+    "created": "2022-03-18T13:01:38.314Z",
+    "modified": "2022-03-18T13:01:38.314Z",
+    "sub": "http://127.0.0.1:8001/",
+    "trust_anchor": 12,
+    "exp": "2022-03-21T10:33:36Z",
+    "iat": "2022-03-18T13:01:38.314Z",
+    "chain": [
+      {
+        "exp": 1648031302,
+        "iat": 1647858502,
+        "iss": "http://127.0.0.1:8001/",
+        "sub": "http://127.0.0.1:8001/",
+        "jwks": {
+          "keys": [
+            {
+              "kty": "RSA",
+              "e": "AQAB",
+              "n": "uXfJA-wTlTCA4FdsoE0qZfmKIgedmarrtWgQbElKbWg9RDR7Z8JVBaRLFqwyfyG1JJFm64G51cBJwLIFwWoF7nxsH9VYLm5ocjAnsR4RhlfVE0y_60wjf8skJgBRpiXQPlwH9jDGaqVE_PEBTObDO5w3XourD1F360-v5cLDLRHdFJIitdEVtqATqY5DglRDaKiBhis7a5_1bk839PDLaQhju4XJk4tvDy5-LVkMy5sP2zU6-1tJdA-VmaBZLXy9n0967FGIWmMzpafrBMOuHFcUOH56o-clDah_CITH1dq2D64K0MYhEpACO2p8AH4K8Q6YuJ1dnkVDDwZp2C84sQ",
+              "kid": "YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs"
+            }
+          ]
+        },
+        "metadata": {
+          "openid_relying_party": {
+            "application_type": "web",
+            "client_id": "http://127.0.0.1:8001/",
+            "client_registration_types": [
+              "automatic"
+            ],
+            "jwks": {
+              "keys": [
+                {
+                  "kty": "RSA",
+                  "use": "sig",
+                  "n": "uXfJA-wTlTCA4FdsoE0qZfmKIgedmarrtWgQbElKbWg9RDR7Z8JVBaRLFqwyfyG1JJFm64G51cBJwLIFwWoF7nxsH9VYLm5ocjAnsR4RhlfVE0y_60wjf8skJgBRpiXQPlwH9jDGaqVE_PEBTObDO5w3XourD1F360-v5cLDLRHdFJIitdEVtqATqY5DglRDaKiBhis7a5_1bk839PDLaQhju4XJk4tvDy5-LVkMy5sP2zU6-1tJdA-VmaBZLXy9n0967FGIWmMzpafrBMOuHFcUOH56o-clDah_CITH1dq2D64K0MYhEpACO2p8AH4K8Q6YuJ1dnkVDDwZp2C84sQ",
+                  "e": "AQAB",
+                  "kid": "YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs"
+                }
+              ]
+            },
+            "client_name": "Name of an example organization",
+            "contacts": [
+              "ops@rp.example.it"
+            ],
+            "grant_types": [
+              "refresh_token",
+              "authorization_code"
+            ],
+            "redirect_uris": [
+              "http://127.0.0.1:8001/oidc/rp/callback"
+            ],
+            "response_types": [
+              "code"
+            ],
+            "subject_type": "pairwise"
+          }
+        },
+        "trust_marks": [
+          {
+            "id": "https://www.spid.gov.it/openid-federation/agreement/sp-public/",
+            "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMifQ.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ1NjEyNDAxLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAiLCJtYXJrIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.mSPNR0AOPBn3UNJAIbrWUMQ8vGTetQajpa3i59JDKDXYWqo2TUGh4AQBghCiG3qqV9cl-hleLtuwoeZ1InKHeslTLftVdcR3meeMLs3mLobHYr26Mi7pC7-jx1ZFVyk4GXl7mn9WVSQGEUOiuhL01tdlUfxf0TJSFSOMEZGpCA3hXroLOnEl3FjkAw7sPvjfImsbadbHVusb72HTTs1n5Xo7z3As3fDWHcxD-fvvq0beu9cx-L2sT4YaNC-ELd1M3m5r0NIjjEUAt4Gnot-l5Z3-C_bA41uvh2hX34U_fGZ6jpmuluJo1Lqi26N8LTB-Rbu0UMaZnkRg9E72_YRZig"
+          }
+        ],
+        "authority_hints": []
+      },
+      {
+        "exp": 1647858816,
+        "iat": 1647856836,
+        "iss": "http://127.0.0.1:8000/",
+        "sub": "http://127.0.0.1:8000/",
+        "jwks": {
+          "keys": [
+            {
+              "kty": "RSA",
+              "n": "3i5vV-_4nF_ES1BU86Zf2Bj6SiyGdGM3Izc2GrvtknQQCzpT3QlGv2d_wMrzVTS7PmZlvjyi2Qceq8EmEwbsIa5R8G57fxSpE0HL33giJfhpe8ublY4hGb6tEqSbHiFcgiF4T-Ft_98pz4nZtKTcesMZ8CcDUd9ibaLXGM4vaiUhSt76X1qOzqJHqAKMG-9VGm5DD2GSe7cu1yvaMCMPU6DGOqHYoBSkSbsnLelsRg6sINh6mZfb39odTJlOMFGhlg665702kc_iqqxd8jpyOh94vBagmJB4EQqI1qEte8sTMeBkVRpSLDoV5uNTlp2ZdINu1SakmaHB3WeStwC1lw",
+              "e": "AQAB",
+              "kid": "FifYx03bnosD8m6gYQIfNHNP9cM_Sam9Tc5nLloIIrc"
+            }
+          ]
+        },
+        "metadata": {
+          "federation_entity": {
+            "contacts": [
+              "ops@localhost"
+            ],
+            "federation_fetch_endpoint": "http://127.0.0.1:8000/fetch/",
+            "federation_resolve_endpoint": "http://127.0.0.1:8000/resolve/",
+            "federation_status_endpoint": "http://127.0.0.1:8000/trust_mark_status/",
+            "homepage_uri": "http://127.0.0.1:8000",
+            "name": "example TA",
+            "federation_list_endpoint": "http://127.0.0.1:8000/list/"
+          }
+        },
+        "trust_marks_issuers": {
+          "https://www.spid.gov.it/certification/rp/public": [
+            "https://registry.spid.agid.gov.it",
+            "https://public.intermediary.spid.it"
+          ],
+          "https://www.spid.gov.it/certification/rp/private": [
+            "https://registry.spid.agid.gov.it",
+            "https://private.other.intermediary.it"
+          ],
+          "https://sgd.aa.it/onboarding": [
+            "https://sgd.aa.it"
+          ]
+        },
+        "constraints": {
+          "max_path_length": 1
+        }
+      },
+      [
+        {
+          "exp": 1648031302,
+          "iat": 1647858502,
+          "iss": "http://127.0.0.1:8000/",
+          "sub": "http://127.0.0.1:8001/",
+          "jwks": {
+            "keys": [
+              {
+                "kty": "RSA",
+                "n": "uXfJA-wTlTCA4FdsoE0qZfmKIgedmarrtWgQbElKbWg9RDR7Z8JVBaRLFqwyfyG1JJFm64G51cBJwLIFwWoF7nxsH9VYLm5ocjAnsR4RhlfVE0y_60wjf8skJgBRpiXQPlwH9jDGaqVE_PEBTObDO5w3XourD1F360-v5cLDLRHdFJIitdEVtqATqY5DglRDaKiBhis7a5_1bk839PDLaQhju4XJk4tvDy5-LVkMy5sP2zU6-1tJdA-VmaBZLXy9n0967FGIWmMzpafrBMOuHFcUOH56o-clDah_CITH1dq2D64K0MYhEpACO2p8AH4K8Q6YuJ1dnkVDDwZp2C84sQ",
+                "e": "AQAB",
+                "kid": "YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs"
+              }
+            ]
+          },
+          "metadata_policy": {
+            "openid_relying_party": {
+              "grant_types": {
+                "subset_of": [
+                  "authorization_code",
+                  "refresh_token"
+                ]
+              },
+              "scopes": {
+                "superset_of": [
+                  "openid"
+                ],
+                "subset_of": [
+                  "openid",
+                  "offline_access"
+                ]
+              }
+            }
+          },
+          "trust_marks": [
+            {
+              "id": "https://www.spid.gov.it/openid-federation/agreement/sp-private",
+              "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAxLyIsImlhdCI6MTY0Nzg1ODUwMiwiaWQiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9jZXJ0aWZpY2F0aW9uL3JwL3ByaXZhdGUiLCJsb2dvX3VyaSI6Imh0dHBzOi8vd3d3LmFnaWQuZ292Lml0L3RoZW1lcy9jdXN0b20vYWdpZC9sb2dvLnN2ZyIsInJlZiI6Imh0dHBzOi8vZG9jcy5pdGFsaWEuaXQvaXRhbGlhL3NwaWQvc3BpZC1yZWdvbGUtdGVjbmljaGUtb2lkYy9pdC9zdGFiaWxlL2luZGV4Lmh0bWwifQ.HJOizHTlrrUrVnILWgXu8-5RC7u5DK2zWMU4ROIfKWEhMDHdoQbtsdx_9Yia6P6f_5wZZs_oavUGeZ8PirFgsDm-zl38YHnR2lBfnGH038jEzhzxPAJmA0D4LaznWzO0ca20GlfWDnLK2z2QT5FzXlXwavAqSHiSfPT6379tANYOnPiIGLhWdVgIlvmbLKxNi310eCxiO_OLVf68pTVZ1QQKlmdNHBvXukGpPUt3tQYuWIk7kDSs6wfURC0yzuGKyzxxXnbl-iHoH7wFbe9bJ1JbmTe_1feuKQFdA6ltJ0TVudCjsp7PfS6Jio7hT35DroNZiRce0_S6aJZEUDTOWg"
+            }
+          ]
+        }
+      ]
+    ],
+    "metadata": {
+      "openid_relying_party": {
+        "application_type": "web",
+        "client_id": "http://127.0.0.1:8001/",
+        "client_registration_types": [
+          "automatic"
+        ],
+        "jwks": {
+          "keys": [
+            {
+              "kty": "RSA",
+              "use": "sig",
+              "n": "uXfJA-wTlTCA4FdsoE0qZfmKIgedmarrtWgQbElKbWg9RDR7Z8JVBaRLFqwyfyG1JJFm64G51cBJwLIFwWoF7nxsH9VYLm5ocjAnsR4RhlfVE0y_60wjf8skJgBRpiXQPlwH9jDGaqVE_PEBTObDO5w3XourD1F360-v5cLDLRHdFJIitdEVtqATqY5DglRDaKiBhis7a5_1bk839PDLaQhju4XJk4tvDy5-LVkMy5sP2zU6-1tJdA-VmaBZLXy9n0967FGIWmMzpafrBMOuHFcUOH56o-clDah_CITH1dq2D64K0MYhEpACO2p8AH4K8Q6YuJ1dnkVDDwZp2C84sQ",
+              "e": "AQAB",
+              "kid": "YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs"
+            }
+          ]
+        },
+        "client_name": "Name of an example organization",
+        "contacts": [
+          "ops@rp.example.it"
+        ],
+        "grant_types": [
+          "refresh_token",
+          "authorization_code"
+        ],
+        "redirect_uris": [
+          "http://127.0.0.1:8001/oidc/rp/callback"
+        ],
+        "response_types": [
+          "code"
+        ],
+        "subject_type": "pairwise"
+      }
+    },
+    "trust_marks": [],
+    "parties_involved": [
+      "http://127.0.0.1:8001/",
+      "http://127.0.0.1:8000/"
+    ],
+    "status": "valid",
+    "log": "",
+    "processing_start": "2022-03-21T10:28:22.609Z",
+    "is_active": true
+  }
+},
+{
+  "model": "spid_cie_oidc_entity.trustchain",
+  "pk": 9,
+  "fields": {
+    "created": "2022-03-21T05:39:18.213Z",
+    "modified": "2022-03-21T05:39:18.213Z",
+    "sub": "http://127.0.0.1:8000/oidc/rp/",
+    "trust_anchor": 12,
+    "exp": "2022-03-21T13:35:12Z",
+    "iat": "2022-03-21T05:39:18.213Z",
+    "chain": [
+      {
+        "exp": 1648040533,
+        "iat": 1647867733,
+        "iss": "http://127.0.0.1:8000/oidc/rp/",
+        "sub": "http://127.0.0.1:8000/oidc/rp/",
+        "jwks": {
+          "keys": [
+            {
+              "kty": "RSA",
+              "n": "5s4qi1Ta-sEuKb5rJ8TzHmyGKaSu89pIXIi6w4Ekx6GL56mJDNE_MWJHsFjWXajfMdMQmZrSXAvLtXxmbhUui9Mq_IormhmEyyEJS0SyE9UKTxWzi0yd_n_C7OjFBhM-0ZyUlgl81E_sr-35P1A6b5WSYwMvRSR-P9yx_NI-XBQ48G_zdmk3CbuuzZsXZqqgj5U7OGWH-4Huosn9nH3FVkwX0OlWkgWM-J9DEWzGBjl9hfbbrMtM_obljHL2NfT6RJYER2IpdI8RCyQS3sMPt6ZHDskmuNlyMDNATCChXQJLnltwEjxcgvzjw_G9J25DwfdfVEhDF_0kCp44UMmS3Q",
+              "e": "AQAB",
+              "kid": "2HnoFS3YnC9tjiCaivhWLVUJ3AxwGGz_98uRFaqMEEs"
+            }
+          ]
+        },
+        "metadata": {
+          "openid_relying_party": {
+            "application_type": "web",
+            "client_id": "http://127.0.0.1:8000/oidc/rp/",
+            "client_registration_types": [
+              "automatic"
+            ],
+            "jwks": {
+              "keys": [
+                {
+                  "kty": "RSA",
+                  "use": "sig",
+                  "n": "5s4qi1Ta-sEuKb5rJ8TzHmyGKaSu89pIXIi6w4Ekx6GL56mJDNE_MWJHsFjWXajfMdMQmZrSXAvLtXxmbhUui9Mq_IormhmEyyEJS0SyE9UKTxWzi0yd_n_C7OjFBhM-0ZyUlgl81E_sr-35P1A6b5WSYwMvRSR-P9yx_NI-XBQ48G_zdmk3CbuuzZsXZqqgj5U7OGWH-4Huosn9nH3FVkwX0OlWkgWM-J9DEWzGBjl9hfbbrMtM_obljHL2NfT6RJYER2IpdI8RCyQS3sMPt6ZHDskmuNlyMDNATCChXQJLnltwEjxcgvzjw_G9J25DwfdfVEhDF_0kCp44UMmS3Q",
+                  "e": "AQAB",
+                  "kid": "2HnoFS3YnC9tjiCaivhWLVUJ3AxwGGz_98uRFaqMEEs"
+                }
+              ]
+            },
+            "client_name": "Name of an example organization",
+            "contacts": [
+              "ops@rp.example.it"
+            ],
+            "grant_types": [
+              "refresh_token",
+              "authorization_code"
+            ],
+            "redirect_uris": [
+              "http://127.0.0.1:8000/oidc/rp/callback"
+            ],
+            "response_types": [
+              "code"
+            ],
+            "subject_type": "pairwise"
+          }
+        },
+        "trust_marks": [
+          {
+            "id": "https://www.spid.gov.it/openid-federation/agreement/sp-public/",
+            "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMifQ.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ1NjEyNDAxLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAiLCJtYXJrIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.mSPNR0AOPBn3UNJAIbrWUMQ8vGTetQajpa3i59JDKDXYWqo2TUGh4AQBghCiG3qqV9cl-hleLtuwoeZ1InKHeslTLftVdcR3meeMLs3mLobHYr26Mi7pC7-jx1ZFVyk4GXl7mn9WVSQGEUOiuhL01tdlUfxf0TJSFSOMEZGpCA3hXroLOnEl3FjkAw7sPvjfImsbadbHVusb72HTTs1n5Xo7z3As3fDWHcxD-fvvq0beu9cx-L2sT4YaNC-ELd1M3m5r0NIjjEUAt4Gnot-l5Z3-C_bA41uvh2hX34U_fGZ6jpmuluJo1Lqi26N8LTB-Rbu0UMaZnkRg9E72_YRZig"
+          }
+        ],
+        "authority_hints": []
+      },
+      {
+        "exp": 1647869712,
+        "iat": 1647867732,
+        "iss": "http://127.0.0.1:8000/",
+        "sub": "http://127.0.0.1:8000/",
+        "jwks": {
+          "keys": [
+            {
+              "kty": "RSA",
+              "n": "3i5vV-_4nF_ES1BU86Zf2Bj6SiyGdGM3Izc2GrvtknQQCzpT3QlGv2d_wMrzVTS7PmZlvjyi2Qceq8EmEwbsIa5R8G57fxSpE0HL33giJfhpe8ublY4hGb6tEqSbHiFcgiF4T-Ft_98pz4nZtKTcesMZ8CcDUd9ibaLXGM4vaiUhSt76X1qOzqJHqAKMG-9VGm5DD2GSe7cu1yvaMCMPU6DGOqHYoBSkSbsnLelsRg6sINh6mZfb39odTJlOMFGhlg665702kc_iqqxd8jpyOh94vBagmJB4EQqI1qEte8sTMeBkVRpSLDoV5uNTlp2ZdINu1SakmaHB3WeStwC1lw",
+              "e": "AQAB",
+              "kid": "FifYx03bnosD8m6gYQIfNHNP9cM_Sam9Tc5nLloIIrc"
+            }
+          ]
+        },
+        "metadata": {
+          "federation_entity": {
+            "contacts": [
+              "ops@localhost"
+            ],
+            "federation_fetch_endpoint": "http://127.0.0.1:8000/fetch/",
+            "federation_resolve_endpoint": "http://127.0.0.1:8000/resolve/",
+            "federation_status_endpoint": "http://127.0.0.1:8000/trust_mark_status/",
+            "homepage_uri": "http://127.0.0.1:8000",
+            "name": "example TA",
+            "federation_list_endpoint": "http://127.0.0.1:8000/list/"
+          }
+        },
+        "trust_marks_issuers": {
+          "https://www.spid.gov.it/certification/rp/public": [
+            "https://registry.spid.agid.gov.it",
+            "https://public.intermediary.spid.it"
+          ],
+          "https://www.spid.gov.it/certification/rp/private": [
+            "https://registry.spid.agid.gov.it",
+            "https://private.other.intermediary.it"
+          ],
+          "https://sgd.aa.it/onboarding": [
+            "https://sgd.aa.it"
+          ]
+        },
+        "constraints": {
+          "max_path_length": 1
+        }
+      },
+      [
+        {
+          "exp": 1648040533,
+          "iat": 1647867733,
+          "iss": "http://127.0.0.1:8000/",
+          "sub": "http://127.0.0.1:8000/oidc/rp/",
+          "jwks": {
+            "keys": [
+              {
+                "kty": "RSA",
+                "n": "5s4qi1Ta-sEuKb5rJ8TzHmyGKaSu89pIXIi6w4Ekx6GL56mJDNE_MWJHsFjWXajfMdMQmZrSXAvLtXxmbhUui9Mq_IormhmEyyEJS0SyE9UKTxWzi0yd_n_C7OjFBhM-0ZyUlgl81E_sr-35P1A6b5WSYwMvRSR-P9yx_NI-XBQ48G_zdmk3CbuuzZsXZqqgj5U7OGWH-4Huosn9nH3FVkwX0OlWkgWM-J9DEWzGBjl9hfbbrMtM_obljHL2NfT6RJYER2IpdI8RCyQS3sMPt6ZHDskmuNlyMDNATCChXQJLnltwEjxcgvzjw_G9J25DwfdfVEhDF_0kCp44UMmS3Q",
+                "e": "AQAB",
+                "kid": "2HnoFS3YnC9tjiCaivhWLVUJ3AxwGGz_98uRFaqMEEs"
+              }
+            ]
+          },
+          "metadata_policy": {
+            "openid_relying_party": {
+              "scopes": {
+                "superset_of": [
+                  "openid"
+                ],
+                "subset_of": [
+                  "openid",
+                  "offline_access",
+                  "profile",
+                  "email"
+                ]
+              },
+              "contacts": {
+                "add": [
+                  "ciao@email.it"
+                ]
+              }
+            }
+          },
+          "trust_marks": [
+            {
+              "id": "https://www.spid.gov.it/openid-federation/agreement/sp-public/",
+              "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ3ODY3NzMzLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAiLCJtYXJrIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.aChWzvPMERsSmdGPNrOcYYJeaeVd7v3ilwOh_IsUkXy7JNvpRaKiexaKqPtveIY8r5hkDK89BCEt16AbXjfUfvXZSbJdeTTnzWUXqDdXHKWZ1010gKLmKMqRuNxeJZzgke4JniuoiXQg0g5AHyXgxqngTfeFRDziegC74NZmeo3IqIR714aIg6-FERPTFqXA2-FgYKTzYvzIZHnWXuW6Iwva2y3w07sD6uqGHMv3_CNtvU_QBLETOJbxy6q04Eq-jYmW1G_JMaB4XKAdYsMXU0jiwSlwLry2-Fc-nAv7wokldEz-0caRgEe5zW0czo-QWCoe-d_QENk8vo9-QzB-Zg"
+            },
+            {
+              "id": "https://www.spid.gov.it/openid-federation/agreement/sp-private",
+              "trust_mark": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29pZGMvcnAvIiwiaWF0IjoxNjQ3ODY3NzMzLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAvcHJpdmF0ZSIsImxvZ29fdXJpIjoiaHR0cHM6Ly93d3cuYWdpZC5nb3YuaXQvdGhlbWVzL2N1c3RvbS9hZ2lkL2xvZ28uc3ZnIiwicmVmIjoiaHR0cHM6Ly9kb2NzLml0YWxpYS5pdC9pdGFsaWEvc3BpZC9zcGlkLXJlZ29sZS10ZWNuaWNoZS1vaWRjL2l0L3N0YWJpbGUvaW5kZXguaHRtbCJ9.WBZ353cr3kWmQZSLC1XLWYf6eYB5DrbyYPgd7GBYM4vT3LzBYDy3ZsK_E2FWZ4VD19juNJ-qtOPbdZ2YHZBgyqyJ2HKLDTlW2Upszdk16RQ5BFbW0y3NqBp1-PZHbFrG-RmHnKcNFxVP9NmD2KGw_LxATMeB17S76GaaHK-mt2yVAGU5RxqF7gvxtPvMM7EYuw03inAPv_SOwYglM1TMLNgt-lvAPbBXVfbUEDyB6qZWm_1AsGh3WQqqaOf8znfT5t66BpGtnQT2cFCvbN-WiAKzkUzDOurdFN7YuLsNt1Mz0U5Y7ddZb6WvB1g-t98x03q8JNEXRKNkY2tH_sCFZA"
+            }
+          ]
+        }
+      ]
+    ],
+    "metadata": {
+      "openid_relying_party": {
+        "application_type": "web",
+        "client_id": "http://127.0.0.1:8000/oidc/rp/",
+        "client_registration_types": [
+          "automatic"
+        ],
+        "jwks": {
+          "keys": [
+            {
+              "kty": "RSA",
+              "use": "sig",
+              "n": "5s4qi1Ta-sEuKb5rJ8TzHmyGKaSu89pIXIi6w4Ekx6GL56mJDNE_MWJHsFjWXajfMdMQmZrSXAvLtXxmbhUui9Mq_IormhmEyyEJS0SyE9UKTxWzi0yd_n_C7OjFBhM-0ZyUlgl81E_sr-35P1A6b5WSYwMvRSR-P9yx_NI-XBQ48G_zdmk3CbuuzZsXZqqgj5U7OGWH-4Huosn9nH3FVkwX0OlWkgWM-J9DEWzGBjl9hfbbrMtM_obljHL2NfT6RJYER2IpdI8RCyQS3sMPt6ZHDskmuNlyMDNATCChXQJLnltwEjxcgvzjw_G9J25DwfdfVEhDF_0kCp44UMmS3Q",
+              "e": "AQAB",
+              "kid": "2HnoFS3YnC9tjiCaivhWLVUJ3AxwGGz_98uRFaqMEEs"
+            }
+          ]
+        },
+        "client_name": "Name of an example organization",
+        "contacts": [
+          "ops@rp.example.it"
+        ],
+        "grant_types": [
+          "refresh_token",
+          "authorization_code"
+        ],
+        "redirect_uris": [
+          "http://127.0.0.1:8000/oidc/rp/callback"
+        ],
+        "response_types": [
+          "code"
+        ],
+        "subject_type": "pairwise"
+      }
+    },
+    "trust_marks": [],
+    "parties_involved": [
+      "http://127.0.0.1:8000/oidc/rp/",
+      "http://127.0.0.1:8000/"
+    ],
+    "status": "valid",
+    "log": "",
+    "processing_start": "2022-03-21T13:02:13.143Z",
+    "is_active": true
+  }
 }
 ]
diff --git a/examples/relying_party/dumps/example.json b/examples/relying_party/dumps/example.json
index ae14efa1..c210700e 100644
--- a/examples/relying_party/dumps/example.json
+++ b/examples/relying_party/dumps/example.json
@@ -462,7 +462,6 @@
     "modified": "2022-02-27T00:50:28.438Z",
     "sub": "http://127.0.0.1:8000/oidc/op/",
     "trust_anchor": 1,
-    "type": "openid_provider",
     "exp": "2022-02-27T01:24:49Z",
     "iat": "2022-02-27T00:50:28.438Z",
     "chain": [
@@ -780,6 +779,7 @@
       ]
     ],
     "metadata": {
+          "openid_provider":{
       "authorization_endpoint": "http://127.0.0.1:8002/oidc/op/authorization",
       "token_endpoint": "http://127.0.0.1:8002/oidc/op/token",
       "userinfo_endpoint": "http://127.0.0.1:8002/oidc/op/userinfo",
@@ -915,7 +915,7 @@
         "ES384",
         "ES512"
       ]
-    },
+    }},
     "trust_marks": [],
     "parties_involved": [
       "http://127.0.0.1:8000/oidc/op/",
diff --git a/spid_cie_oidc/__init__.py b/spid_cie_oidc/__init__.py
index 6ff6db18..3d187266 100644
--- a/spid_cie_oidc/__init__.py
+++ b/spid_cie_oidc/__init__.py
@@ -1,1 +1,1 @@
-__version__ = "0.4.16"
+__version__ = "0.5.0"
diff --git a/spid_cie_oidc/authority/admin.py b/spid_cie_oidc/authority/admin.py
index 8f62f0ad..c3dc136e 100644
--- a/spid_cie_oidc/authority/admin.py
+++ b/spid_cie_oidc/authority/admin.py
@@ -4,7 +4,8 @@
     FederationDescendant,
     FederationDescendantContact,
     FederationEntityProfile,
-    FederationEntityAssignedProfile
+    FederationEntityAssignedProfile,
+    StaffToken
 )
 
 
@@ -34,10 +35,19 @@ class FederationDescendantAdmin(admin.ModelAdmin):
 class FederationEntityProfileAdmin(admin.ModelAdmin):
     list_display = ("name", "profile_id")
     list_filter = ("created", "modified")
-    search_fields = ("name", "profile_id", "created", "modified")
+    search_fields = ("name", "profile_id",)
     readonly_fields = ("trust_mark_template_as_json",)
 
 
+@admin.register(StaffToken)
+class StaffTokenAdmin(admin.ModelAdmin):
+    list_display = ("user", "expire_at", "is_valid")
+    list_filter = ("created", "modified", "expire_at")
+    search_fields = ("token", )
+    readonly_fields = ("is_valid",)
+    raw_id_fields = ('user',)
+
+
 @admin.register(FederationEntityAssignedProfile)
 class FederationEntityAssignedProfileAdmin(admin.ModelAdmin):
     list_display = ("descendant", "profile", "issuer", "created")
diff --git a/spid_cie_oidc/authority/migrations/0006_stafftoken.py b/spid_cie_oidc/authority/migrations/0006_stafftoken.py
new file mode 100644
index 00000000..5d9ad3df
--- /dev/null
+++ b/spid_cie_oidc/authority/migrations/0006_stafftoken.py
@@ -0,0 +1,32 @@
+# Generated by Django 4.0.3 on 2022-03-20 10:50
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import spid_cie_oidc.authority.utils
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('spid_cie_oidc_authority', '0005_alter_federationdescendant_jwks'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='StaffToken',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('modified', models.DateTimeField(auto_now=True)),
+                ('token', models.CharField(default=spid_cie_oidc.authority.utils.random_token, help_text='any kind of contact type, usually an email.', max_length=255)),
+                ('is_active', models.BooleanField(default=True)),
+                ('user', models.ForeignKey(help_text='The user responsible of thi token', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'verbose_name': 'Staff Token',
+                'verbose_name_plural': 'Staff Tokens',
+            },
+        ),
+    ]
diff --git a/spid_cie_oidc/authority/migrations/0007_stafftoken_expire_at.py b/spid_cie_oidc/authority/migrations/0007_stafftoken_expire_at.py
new file mode 100644
index 00000000..eb31f111
--- /dev/null
+++ b/spid_cie_oidc/authority/migrations/0007_stafftoken_expire_at.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.0.3 on 2022-03-20 10:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('spid_cie_oidc_authority', '0006_stafftoken'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='stafftoken',
+            name='expire_at',
+            field=models.DateTimeField(blank=True, null=True),
+        ),
+    ]
diff --git a/spid_cie_oidc/authority/models.py b/spid_cie_oidc/authority/models.py
index 931c83ec..e928fc0f 100644
--- a/spid_cie_oidc/authority/models.py
+++ b/spid_cie_oidc/authority/models.py
@@ -4,7 +4,9 @@
 from django.db import models
 
 # from django.db.models.signals import post_save
+from django.utils import timezone
 from django.utils.translation import gettext as _
+from spid_cie_oidc.authority.utils import random_token
 
 from spid_cie_oidc.entity.abstract_models import TimeStampedModel
 from spid_cie_oidc.entity.models import (
@@ -308,6 +310,47 @@ def __str__(self):
         return f"{self.contact} {self.entity.sub}"
 
 
+class StaffToken(TimeStampedModel):
+    """
+        Token provisioned to staffs operators for protected resources
+    """
+
+    user = models.ForeignKey(
+        get_user_model(),
+        on_delete=models.CASCADE,
+        help_text=_("The user responsible of thi token"),
+    )
+    token = models.CharField(
+        max_length=255,
+        blank=False,
+        null=False,
+        default = random_token,
+        help_text=_("any kind of contact type, usually an email."),
+    )
+    expire_at = models.DateTimeField(blank=True, null=True)
+    is_active = models.BooleanField(
+        default=True,
+        blank=False,
+        null=False
+    )
+
+    class Meta:
+        verbose_name = "Staff Token"
+        verbose_name_plural = "Staff Tokens"
+
+    @property
+    def is_valid(self):
+        if self.is_active and not self.expire_at:
+            return True
+        elif self.is_active and self.expire_at > timezone.localtime():
+            return True
+        else:
+            return False
+
+    def __str__(self):
+        return f"{self.user} {self.is_active}"
+
+
 # signal on each save
 # def trust_chain_trigger(**kwargs):
 # subject = kwargs['instance'].sub
diff --git a/spid_cie_oidc/authority/settings.py b/spid_cie_oidc/authority/settings.py
index 079e0454..42c67f45 100644
--- a/spid_cie_oidc/authority/settings.py
+++ b/spid_cie_oidc/authority/settings.py
@@ -22,10 +22,6 @@
 FEDERATION_DEFAULT_POLICY = getattr(
     settings, "FEDERATION_DEFAULT_POLICY", {
         "openid_provider": {
-            # TODO: be dynamic
-            "contacts": {"add": ["$.contacts"]},
-            # "op_name": "SPID OIDC Identity Provider",
-            # "organization_name": { "value": "SPID OIDC Identity Provider" },
             "subject_types_supported": {"value": ["pairwise"]},
             "id_token_signing_alg_values_supported": {
                 "subset_of": SIGNING_ALG_VALUES_SUPPORTED
@@ -51,28 +47,6 @@
             },
         },
         "openid_relying_party": {
-            # TODO: be dynamic -> to be customized for each entities, not something to default!
-            # "client_id": {"value":  "https://rp.example.it/spid"},
-            # "redirect_uris": {
-            #   "subset_of": [
-            #     "https://rp.example.it/spid/cb1",
-            #     "https://rp.example.it/spid/cb2"
-            #   ]
-            # },
-            # "organization_name": {"value": "Example RP organization name"},
-            # "logo_uri": {
-            #   "one_of": [
-            #   "https://rp.example.it/logo_small.jpg",
-            #   "https://rp.example.it/logo_big.jpg"
-            #  ],
-            # "default": "https://rp.example.it/logo_small.jpg"
-            # },
-            # "policy_uri": {
-            #   "value": "https://rp.example.it/policy.html"
-            # },
-            # "tos_uri": {
-            # "value": "https://rp.example.it/tos.html"
-            # },
             "grant_types": {"subset_of": ["authorization_code", "refresh_token"]},
             "scopes": {
                 "superset_of": ["openid"],
diff --git a/spid_cie_oidc/authority/utils.py b/spid_cie_oidc/authority/utils.py
new file mode 100644
index 00000000..a93d11da
--- /dev/null
+++ b/spid_cie_oidc/authority/utils.py
@@ -0,0 +1,5 @@
+from secrets import token_hex
+
+
+def random_token(n=254):
+    return token_hex(n)
diff --git a/spid_cie_oidc/authority/views.py b/spid_cie_oidc/authority/views.py
index c1c65105..e553ce0a 100644
--- a/spid_cie_oidc/authority/views.py
+++ b/spid_cie_oidc/authority/views.py
@@ -14,6 +14,7 @@
 from spid_cie_oidc.authority.models import (
     FederationDescendant,
     FederationEntityAssignedProfile,
+    StaffToken,
     get_first_self_trust_anchor
 )
 from spid_cie_oidc.authority.settings import MAX_ENTRIES_PAGE
@@ -139,6 +140,7 @@ def resolve_entity_statement(request, format: str = "jose"):
     Metadata if it's valid
     we avoid any possibility to trigger a new Metadata discovery if
     """
+
     if not all((request.GET.get("sub", None), request.GET.get("anchor", None))):
         raise Http404("sub and anchor parameters are REQUIRED.")
 
@@ -152,37 +154,34 @@ def resolve_entity_statement(request, format: str = "jose"):
         trust_anchor__sub=request.GET["anchor"],
         is_active=True
     )
-    entity = TrustChain.objects.filter(**_q)
 
-    if not entity:
-        raise Http404("entity not found.")
-    elif entity and request.GET.get("type", None):
-        _q["type"] = request.GET["type"]
-        typed_entity = entity.filter(type=request.GET["type"]).first()
-        if not typed_entity:
-            logger.warning(
-                f'Resolve statement endpoint not found for {request.GET["sub"]} '
-                f'with metadata_type {request.GET["type"]}.'
-            )
-            raise Http404("entity metadata type not found.")
-        else:
-            entity = typed_entity
+    # gets the cached one
+    entity = TrustChain.objects.filter(**_q).first()
+
+    # only with privileged actors with staff token can triggers a new trust chain
+    staff_token_head = request.headers.get("Authorization", None)
+    if staff_token_head:
+        staff_token = StaffToken.objects.filter(
+            token = staff_token_head
+        ).first()
+        if staff_token.is_valid:
+            try:
+                # a staff token get a fresh trust chain on each call
+                entity = get_or_create_trust_chain(
+                    httpc_params=HTTPC_PARAMS,
+                    required_trust_marks = getattr(
+                        settings, "OIDCFED_REQUIRED_TRUST_MARKS", []
+                    ),
+                    subject=_q["sub"],
+                    trust_anchor=_q["trust_anchor__sub"],
+                    force = True
+                )
+            except Exception as e:
+                logger.error(
+                    f"Failed privileged Trust Chain creation for {_q['sub']}: {e}"
+                )
 
-    try:
-        tc_data = dict(
-            httpc_params=HTTPC_PARAMS,
-            # TODO
-            # required_trust_marks = [],
-            subject=_q["sub"],
-            trust_anchor=_q["trust_anchor__sub"]
-        )
-        if _q.get('type', None):
-            tc_data["metadata_type"] = _q['type']
-        entity = get_or_create_trust_chain(**tc_data)
-    except Exception as e:
-        logger.error(
-            f"Failed Trust Chain on resolve statement endpoint: {e}"
-        )
+    if not entity:
         raise Http404("entity not found.")
 
     res = {
diff --git a/spid_cie_oidc/entity/admin.py b/spid_cie_oidc/entity/admin.py
index cfa28fb1..0e7ca557 100644
--- a/spid_cie_oidc/entity/admin.py
+++ b/spid_cie_oidc/entity/admin.py
@@ -1,14 +1,16 @@
 from django.contrib import admin
 from django.utils.safestring import mark_safe
+from django.contrib import messages
 
 # from prettyjson import PrettyJSONWidget
 
-
+from django.conf import settings
 from .models import (
     FederationEntityConfiguration,
     FetchedEntityStatement,
     TrustChain
 )
+from spid_cie_oidc.entity.trust_chain_operations import get_or_create_trust_chain
 
 
 @admin.register(FederationEntityConfiguration)
@@ -55,8 +57,28 @@ def pems_as_html(self, obj):
 
 @admin.register(TrustChain)
 class TrustChainAdmin(admin.ModelAdmin):
-    list_display = ("sub", "type", "exp", "modified", "is_valid")
-    list_filter = ("exp", "modified", "is_active", "type")
+
+    @admin.action(description='reload trust chain')
+    def update_trust_chain(modeladmin, request, queryset):
+        for tc in queryset:
+            sub = tc.sub
+            ta = tc.trust_anchor.sub
+            try :
+                get_or_create_trust_chain(
+                        subject=sub,
+                        trust_anchor=ta,
+                        httpc_params=settings.HTTPC_PARAMS,
+                        required_trust_marks=getattr(
+                            settings, "OIDCFED_REQUIRED_TRUST_MARKS", []
+                        ),
+                )
+                messages.success(request, f"reload trust chain successfully")
+            except Exception as e:
+                messages.error(request, f"Failed to update {sub} due to: {e}")
+                continue
+
+    list_display = ("sub", "exp", "modified", "is_valid")
+    list_filter = ("exp", "modified", "is_active")
     search_fields = ("sub",)
     readonly_fields = (
         "created",
@@ -69,6 +91,8 @@ class TrustChainAdmin(admin.ModelAdmin):
         "iat",
     )
 
+    actions = [update_trust_chain]
+
 
 @admin.register(FetchedEntityStatement)
 class FetchedEntityStatementAdmin(admin.ModelAdmin):
diff --git a/spid_cie_oidc/entity/jwtse.py b/spid_cie_oidc/entity/jwtse.py
index 80d32acb..fc913c7e 100644
--- a/spid_cie_oidc/entity/jwtse.py
+++ b/spid_cie_oidc/entity/jwtse.py
@@ -89,7 +89,9 @@ def verify_jws(jws: str, pub_jwk: dict, **kwargs) -> str:
 
     _head = unpad_jwt_head(jws)
     if _head.get("kid") != pub_jwk["kid"]:
-        raise Exception(f"kid error: {_head.get('kid')} != {pub_jwk['kid']}")
+        raise Exception(
+            f"kid error: {_head.get('kid')} != {pub_jwk['kid']}"
+        )
 
     _alg = _head["alg"]
     if _alg not in SIGNING_ALG_VALUES_SUPPORTED or not _alg:
diff --git a/spid_cie_oidc/entity/migrations/0014_alter_trustchain_unique_together_and_more.py b/spid_cie_oidc/entity/migrations/0014_alter_trustchain_unique_together_and_more.py
new file mode 100644
index 00000000..c5801be4
--- /dev/null
+++ b/spid_cie_oidc/entity/migrations/0014_alter_trustchain_unique_together_and_more.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.0.3 on 2022-03-20 13:27
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('spid_cie_oidc_entity', '0013_alter_federationentityconfiguration_metadata'),
+    ]
+
+    operations = [
+        migrations.AlterUniqueTogether(
+            name='trustchain',
+            unique_together={('sub', 'trust_anchor')},
+        ),
+        migrations.RemoveField(
+            model_name='trustchain',
+            name='type',
+        ),
+    ]
diff --git a/spid_cie_oidc/entity/migrations/0015_alter_trustchain_unique_together_trustchain_type_and_more.py b/spid_cie_oidc/entity/migrations/0015_alter_trustchain_unique_together_trustchain_type_and_more.py
new file mode 100644
index 00000000..c315d15a
--- /dev/null
+++ b/spid_cie_oidc/entity/migrations/0015_alter_trustchain_unique_together_trustchain_type_and_more.py
@@ -0,0 +1,26 @@
+# Generated by Django 4.0.3 on 2022-03-20 13:45
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('spid_cie_oidc_entity', '0014_alter_trustchain_unique_together_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterUniqueTogether(
+            name='trustchain',
+            unique_together=set(),
+        ),
+        migrations.AddField(
+            model_name='trustchain',
+            name='type',
+            field=models.CharField(blank=True, choices=[('federation_entity', 'federation_entity'), ('openid_relying_party', 'openid_relying_party'), ('openid_provider', 'openid_provider'), ('oauth_resource', 'oauth_resource')], default='openid_provider', help_text='OpenID Connect Federation entity type', max_length=33),
+        ),
+        migrations.AlterUniqueTogether(
+            name='trustchain',
+            unique_together={('sub', 'type', 'trust_anchor')},
+        ),
+    ]
diff --git a/spid_cie_oidc/entity/migrations/0016_alter_trustchain_unique_together_and_more.py b/spid_cie_oidc/entity/migrations/0016_alter_trustchain_unique_together_and_more.py
new file mode 100644
index 00000000..bb5e48ce
--- /dev/null
+++ b/spid_cie_oidc/entity/migrations/0016_alter_trustchain_unique_together_and_more.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.0.3 on 2022-03-20 14:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('spid_cie_oidc_entity', '0015_alter_trustchain_unique_together_trustchain_type_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterUniqueTogether(
+            name='trustchain',
+            unique_together={('sub', 'trust_anchor')},
+        ),
+        migrations.RemoveField(
+            model_name='trustchain',
+            name='type',
+        ),
+    ]
diff --git a/spid_cie_oidc/entity/models.py b/spid_cie_oidc/entity/models.py
index 312950ac..01aaf1b6 100644
--- a/spid_cie_oidc/entity/models.py
+++ b/spid_cie_oidc/entity/models.py
@@ -293,13 +293,6 @@ class TrustChain(TimeStampedModel):
         help_text=_("URL that identifies this Entity in the Federation. "),
     )
     trust_anchor = models.ForeignKey(FetchedEntityStatement, on_delete=models.CASCADE)
-    type = models.CharField(
-        max_length=33,
-        blank=True,
-        default="openid_provider",
-        choices=[(i, i) for i in ENTITY_TYPES],
-        help_text=_("OpenID Connect Federation entity type"),
-    )
     exp = models.DateTimeField()
     iat = models.DateTimeField(auto_now_add=True)
     chain = models.JSONField(
@@ -347,7 +340,7 @@ class TrustChain(TimeStampedModel):
     class Meta:
         verbose_name = "Trust Chain"
         verbose_name_plural = "Trust Chains"
-        unique_together = ("sub", "type", "trust_anchor")
+        unique_together = ("sub", "trust_anchor")
 
     @property
     def subject(self):
@@ -372,4 +365,6 @@ def is_valid(self):
     # TODO: property is_expired
 
     def __str__(self):
-        return "{} [{}] [{}]".format(self.sub, self.type, self.is_valid)
+        return "{} [{}] [{}]".format(
+            self.sub, self.trust_anchor, self.is_valid
+        )
diff --git a/spid_cie_oidc/entity/schemas/op_metadata.py b/spid_cie_oidc/entity/schemas/op_metadata.py
index d6292054..05a29be1 100644
--- a/spid_cie_oidc/entity/schemas/op_metadata.py
+++ b/spid_cie_oidc/entity/schemas/op_metadata.py
@@ -25,18 +25,12 @@ class GrantTypeSupported(str, Enum):
     authorization_code = "authorization_code"
 
 
-class AcrValuesSupportedSpid(str, Enum):
+class AcrValuesSupported(str, Enum):
     spid_l1 = "https://www.spid.gov.it/SpidL1"
     spid_l2 = "https://www.spid.gov.it/SpidL2"
     spid_l3 = "https://www.spid.gov.it/SpidL3"
 
 
-class AcrValuesSupportedCie(str, Enum):
-    cie_l1 = "CIE_L1"
-    cie_l2 = "CIE_L2"
-    cie_l3 = "CIE_L3"
-
-
 class SigningAlgValuesSupported(str, Enum):
     es256 = "ES256"
     es384 = "ES384"
@@ -112,6 +106,7 @@ class OPMetadata(BaseModel):
     token_endpoint_auth_methods_supported = ["private_key_jwt"]
     subject_types_supported = ["pairwise"]
     request_parameter_supported = True
+    acr_values_supported: List[AcrValuesSupported]
 
 
 class OPMetadataCie(OPMetadata):
@@ -121,7 +116,6 @@ class OPMetadataCie(OPMetadata):
     response_types_supported = ["code"]
     response_modes_supported: List[ResponseModesSupported]
     grant_types_supported: List[GrantTypeSupported]
-    acr_values_supported: List[AcrValuesSupportedCie]
     claims_supported: List[ClaimsSupported]
     claims_parameter_supported = True
     tls_client_certificate_bound_access_tokens = True
@@ -143,7 +137,6 @@ class OPMetadataSpid(OPMetadata):
     op_name: str
     # TODO: Could be specified in multiple languages
     op_uri: str
-    acr_values_supported: List[AcrValuesSupportedSpid]
 
     @validator("jwks_uri")
     def validate_jwks_uri(cls, jwks_uri, values):
diff --git a/spid_cie_oidc/entity/statements.py b/spid_cie_oidc/entity/statements.py
index 0706a167..27831a9c 100644
--- a/spid_cie_oidc/entity/statements.py
+++ b/spid_cie_oidc/entity/statements.py
@@ -338,20 +338,11 @@ def get_superiors(
             target[ec.payload["sub"]] = ec
 
         for ahints in authority_hints:
-            ec = target.get(ahints)
-            if not ec:
+            if not self.verified_superiors.get(ahints, None):
                 logger.warning(
                     f"{ahints} is not available, missing or not valid authority hint"
                 )
                 continue
-            # TODO: this is a copy/pasted code with the previous for statement
-            # TODO: it must be generalized and merged with the previous one
-            if ec.validate_by_itself():
-                target = self.verified_superiors
-            else:
-                target = self.failed_superiors
-
-            target[ec.payload["sub"]] = ec
 
         return self.verified_superiors
 
diff --git a/spid_cie_oidc/entity/trust_chain.py b/spid_cie_oidc/entity/trust_chain.py
index 48fba253..c84c9fa2 100644
--- a/spid_cie_oidc/entity/trust_chain.py
+++ b/spid_cie_oidc/entity/trust_chain.py
@@ -42,7 +42,6 @@ def __init__(
         max_authority_hints: int = 10,
         subject_configuration: EntityConfiguration = None,
         required_trust_marks: list = [],
-        metadata_type="openid_provider",
         # TODO - prefetch cache?
         # pre_fetched_entity_configurations = {},
         # pre_fetched_statements = {},
@@ -67,8 +66,6 @@ def __init__(
 
         # dynamically valued
         self.max_path_len = 0
-
-        self.metadata_type = metadata_type
         self.final_metadata: dict = {}
 
         self.verified_trust_marks = []
@@ -117,13 +114,10 @@ def apply_metadata_policy(self) -> dict:
         # once I filtered a concrete and unique trust path I can apply the metadata policy
         if path_found:
             logger.info(f"Found a trust path: {self.trust_path}")
-            self.final_metadata = self.subject_configuration.payload.get(
-                "metadata", {}
-            ).get(self.metadata_type)
+            self.final_metadata = self.subject_configuration.payload.get("metadata", {})
             if not self.final_metadata:
                 logger.error(
-                    f"Missing {self.metadata_type} in "
-                    f"{self.subject_configuration.payload['metadata']}"
+                    f"Missing metadata in {self.subject_configuration.payload['metadata']}"
                 )
                 return
 
@@ -132,9 +126,13 @@ def apply_metadata_policy(self) -> dict:
                 _pol = (
                     self.trust_path[i]
                     .verified_descendant_statements.get("metadata_policy", {})
-                    .get(self.metadata_type, {})
                 )
-                self.final_metadata = apply_policy(self.final_metadata, _pol)
+                for md_type, md in _pol.items():
+                    if not self.final_metadata.get(md_type):
+                        continue
+                    self.final_metadata[md_type] = apply_policy(
+                        self.final_metadata[md_type], _pol[md_type]
+                    )
 
         # set exp
         self.set_exp()
diff --git a/spid_cie_oidc/entity/trust_chain_operations.py b/spid_cie_oidc/entity/trust_chain_operations.py
index d24a6a38..424b25d8 100644
--- a/spid_cie_oidc/entity/trust_chain_operations.py
+++ b/spid_cie_oidc/entity/trust_chain_operations.py
@@ -17,23 +17,16 @@ def trust_chain_builder(
     subject: str,
     trust_anchor: EntityConfiguration,
     httpc_params: dict = HTTPC_PARAMS,
-    required_trust_marks: list = [],
-    metadata_type: str = "openid_provider",
+    required_trust_marks: list = []
 ) -> Union[TrustChainBuilder, bool]:
     """
-    Minimal Provider Discovery endpoint request processing
-
-    metadata_type MUST be one of
-        openid_provider
-        openid_relying_party
-        oauth_resource
+        Trust Chain builder
     """
     tc = TrustChainBuilder(
         subject,
         trust_anchor=trust_anchor,
         required_trust_marks=required_trust_marks,
-        httpc_params=httpc_params,
-        metadata_type=metadata_type,
+        httpc_params=httpc_params
     )
     tc.start()
 
@@ -105,7 +98,6 @@ def get_or_create_trust_chain(
     trust_anchor: str,
     httpc_params: dict = HTTPC_PARAMS,
     required_trust_marks: list = [],
-    metadata_type: str = "openid_provider",
     force: bool = False,
 ) -> Union[TrustChain, None]:
     """
@@ -161,8 +153,7 @@ def get_or_create_trust_chain(
         trust_chain = trust_chain_builder(
             subject=subject,
             trust_anchor=ta_conf,
-            required_trust_marks=required_trust_marks,
-            metadata_type=metadata_type
+            required_trust_marks=required_trust_marks
         )
         if not trust_chain or not trust_chain.is_valid:
             raise InvalidTrustchain(
@@ -172,13 +163,12 @@ def get_or_create_trust_chain(
         elif not trust_chain.final_metadata:
             raise TrustchainMissingMetadata(
                 f"Trust chain for subject {subject} and "
-                f"trust_anchor {trust_anchor} doesn't have any "
-                f"metadata of type '{metadata_type}'"
+                f"trust_anchor {trust_anchor} doesn't have any metadata"
             )
         dumps_statements_from_trust_chain_to_db(trust_chain)
 
         tc = TrustChain.objects.filter(
-            sub=subject, type=metadata_type, trust_anchor__sub=trust_anchor
+            sub=subject, trust_anchor__sub=trust_anchor
         )
 
         data = dict(
@@ -201,7 +191,6 @@ def get_or_create_trust_chain(
         else:
             tc = TrustChain.objects.create(
                 sub=subject,
-                type=metadata_type,
                 trust_anchor=fetched_trust_anchor,
                 **data,
             )
diff --git a/spid_cie_oidc/onboarding/admin.py b/spid_cie_oidc/onboarding/admin.py
index 86bddd2f..a090b978 100644
--- a/spid_cie_oidc/onboarding/admin.py
+++ b/spid_cie_oidc/onboarding/admin.py
@@ -3,7 +3,7 @@
 from django.db import IntegrityError
 
 from .models import OnBoardingRegistration
-from spid_cie_oidc.authority.models import FederationDescendant
+from spid_cie_oidc.authority.models import FederationDescendant, FederationDescendantContact
 
 
 @admin.register(OnBoardingRegistration)
@@ -16,17 +16,24 @@ def make_published(modeladmin, request, queryset):
         sub = entity_onboarded.url_entity
         jwks = entity_onboarded.public_jwks
         _type = entity_onboarded.type
+        contact = entity_onboarded.contact
 
         try:
-            FederationDescendant.objects.create(
+            entity = FederationDescendant.objects.create(
                 name = name,
                 sub = sub,
                 type = _type,
                 jwks = jwks,
                 is_active = True,
+                status = "valid"
+            )
+            FederationDescendantContact.objects.create(
+                entity = entity,
+                contact = contact,
+                type = "email"
             )
         except IntegrityError:
-            messages.error(request, f"Already exists a descendant with subject: {sub}" )
+            messages.error(request, f"Already exists a descendant with subject: {sub}")
 
     list_display = (
         "organization_name",
diff --git a/spid_cie_oidc/onboarding/forms.py b/spid_cie_oidc/onboarding/forms.py
index a8f8915d..6c02ece0 100644
--- a/spid_cie_oidc/onboarding/forms.py
+++ b/spid_cie_oidc/onboarding/forms.py
@@ -43,7 +43,13 @@ class OnboardingRegistrationForm(forms.Form):
                 "Enter the url of the page where the SPID/CIE button is available"
             )
         },
-    ),
+    )
+
+    contact = forms.EmailField(
+        initial="",
+        label=_("contact email"),
+        error_messages={"required": _("Enter your contact email")},
+    )
 
     type = forms.ChoiceField(
         choices=[(i, i) for i in ENTITY_TYPES],
@@ -99,3 +105,18 @@ class OnboardingValidatingTrustMarkForm(forms.Form):
         label=_("Enter trust mark"),
         required=False
     )
+
+
+class OnboardingDecodeForm(forms.Form):
+
+    jwt = forms.CharField(
+        initial="",
+        label=_("jwt"),
+        error_messages={"required": _("Enter a jwt")},
+    )
+
+    jwk = forms.JSONField(
+        initial=dict,
+        label=_("jwk"),
+        required=False,
+    )
diff --git a/spid_cie_oidc/onboarding/migrations/0007_onboardingregistration_contact.py b/spid_cie_oidc/onboarding/migrations/0007_onboardingregistration_contact.py
new file mode 100644
index 00000000..d2637e6b
--- /dev/null
+++ b/spid_cie_oidc/onboarding/migrations/0007_onboardingregistration_contact.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.0.2 on 2022-03-22 16:50
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('spid_cie_oidc_onboarding', '0006_onboardingregistration_type'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='onboardingregistration',
+            name='contact',
+            field=models.CharField(default=datetime.date(2022, 3, 22), help_text='any kind of contact type, usually an email.', max_length=255),
+            preserve_default=False,
+        ),
+    ]
diff --git a/spid_cie_oidc/onboarding/models.py b/spid_cie_oidc/onboarding/models.py
index 17de41ae..42e59995 100644
--- a/spid_cie_oidc/onboarding/models.py
+++ b/spid_cie_oidc/onboarding/models.py
@@ -43,6 +43,13 @@ class OnBoardingRegistration(TimeStampedModel):
         help_text=_("OpenID Connect Federation entity type"),
     )
 
+    contact = models.CharField(
+        max_length=255,
+        blank=False,
+        null=False,
+        help_text=_("any kind of contact type, usually an email."),
+    )
+
     status = models.CharField(
         max_length=33,
         choices=(
diff --git a/spid_cie_oidc/onboarding/schemas/authn_requests.py b/spid_cie_oidc/onboarding/schemas/authn_requests.py
index d9d88ace..5632f5e4 100644
--- a/spid_cie_oidc/onboarding/schemas/authn_requests.py
+++ b/spid_cie_oidc/onboarding/schemas/authn_requests.py
@@ -6,18 +6,12 @@
 # TODO: Schema of claims is not genrated
 
 
-class AcrValuesSpid(str, Enum):
+class AcrValues(str, Enum):
     l1 = "https://www.spid.gov.it/SpidL1"
     l2 = "https://www.spid.gov.it/SpidL2"
     l3 = "https://www.spid.gov.it/SpidL3"
 
 
-class AcrValuesCie(str, Enum):
-    l1 = "CIE_L1"
-    l2 = "CIE_L2"
-    l3 = "CIE_L3"
-
-
 class ScopeSpid(str, Enum):
     openid = "openid"
     offline_access = "offline_access"
@@ -152,6 +146,7 @@ class AuthenticationRequest(BaseModel):
     exp: Optional[int]
     jti: Optional[str]
     aud: List[HttpUrl]
+    acr_values: List[AcrValues]
 
     @validator("claims")
     def validate_claims(cls, claims):
@@ -176,7 +171,6 @@ def validate_scope(cls, scope):
 class AuthenticationRequestSpid(AuthenticationRequest):
     scope: List[ScopeSpid]
     prompt: Literal["consent", "consent login", "verify"]
-    acr_values: List[AcrValuesSpid]
 
     def get_claims() -> dict:
         return CLAIMS_SPID
@@ -185,7 +179,6 @@ def get_claims() -> dict:
 class AuthenticationRequestCie(AuthenticationRequest):
     scope: List[ScopeCie]
     prompt: Literal["consent", "consent login"]
-    acr_values: List[AcrValuesCie]
 
     def get_claims() -> dict:
         return CLAIMS_CIE
diff --git a/spid_cie_oidc/onboarding/templates/onboarding_decode_jwt.html b/spid_cie_oidc/onboarding/templates/onboarding_decode_jwt.html
index 2b4603de..34a37489 100644
--- a/spid_cie_oidc/onboarding/templates/onboarding_decode_jwt.html
+++ b/spid_cie_oidc/onboarding/templates/onboarding_decode_jwt.html
@@ -17,26 +17,34 @@ <h4 class="text-left">
         {% csrf_token %}
         <div class="row">
             <div class="col-12 col-lg-7">
+                {% csrf_token %}
+                {% for form_field in form %}
                 <div class="form-row">
+                    {% if form_field.name == 'jwt' %}
                     <div class="col-12 form-group my-3">
-                        <label for="id_jwt">
+                        <label for="id_{{ form_field.html_name }}">
                             <span class="form-label-content">
-                                Jwt
+                            {{ form_field.label }}
                             </span>
                         </label>
-                        <textarea id="id_jwt" rows="14" name="jwt">{{ jwt }}</textarea>
+                        <textarea id="id_{{ form_field.html_name }}" rows="14" 
+                        name="{{ form_field.name }}">{{ form.jwt.value}}</textarea>
                     </div>
-                </div>
-                <div class="form-row">
+                    {% else %}
                     <div class="col-12 form-group my-3">
-                        <label for="id_jwk">
-                            <span class="form-label-content">
-                                Jwk
+                        <label for="id_{{ form_field.html_name }}">
+                            <span class="form-label-content mt-4">
+                            {{ form_field.label }}
                             </span>
                         </label>
-                        <textarea id="id_jwk" rows="6" name="jwk">{{ jwk }}</textarea>
+                        <textarea id="id_{{ form_field.html_name }}" rows="6" 
+                        name="{{ form_field.name }}">{{ form.jwk.value }}</textarea>
                     </div>
+                    
+                    {% endif %}
+                    <span for="id_{{ form_field.html_name }}" class="text-danger">{{ form_field.errors }}</span>
                 </div>
+                {% endfor %}
             </div>
             <div class="col-12 col-lg-5">
                 <div class="form-row">
diff --git a/spid_cie_oidc/onboarding/views.py b/spid_cie_oidc/onboarding/views.py
index 823b6d46..6ba00dac 100644
--- a/spid_cie_oidc/onboarding/views.py
+++ b/spid_cie_oidc/onboarding/views.py
@@ -8,7 +8,8 @@
 from .forms import (
     OnboardingRegistrationForm,
     OnboardingCreateTrustChain,
-    OnboardingValidatingTrustMarkForm
+    OnboardingValidatingTrustMarkForm,
+    OnboardingDecodeForm
 )
 from .models import OnBoardingRegistration
 from spid_cie_oidc.entity.jwks import (
@@ -255,31 +256,28 @@ def onboarding_validate_ec(request):
 
 
 def onboarding_decode_jwt(request):
-    context = {
-            "jwt": "",
-            "jwk": "",
-            "head": "",
-            "payload": ""
-    }
-    if request.POST.get('jwt'):
-        jwt = request.POST['jwt']
-        head = unpad_jwt_head(jwt)
-        payload = unpad_jwt_payload(jwt)
-        context["jwt"] = jwt
-        context["head"] = json.dumps(head, indent=4)
-        context["payload"] = json.dumps(payload, indent=4)
-        if request.POST.get('jwk'):
-            jwk_str = request.POST['jwk']
-            context["jwk"] = jwk_str
-            jwk_str_double_quote = jwk_str.replace("'", '"')
-            try:
-                jwk = json.loads(jwk_str_double_quote)
+    if request.method == "GET":
+        form = OnboardingDecodeForm()
+    else:
+        form = OnboardingDecodeForm(request.POST)
+
+    context = {'form': form}
+
+    if form.is_valid():
+        form_dict = {**form.cleaned_data}
+        jwt = form_dict['jwt']
+        try:
+            head = unpad_jwt_head(jwt)
+            payload = unpad_jwt_payload(jwt)
+            context["head"] = json.dumps(head, indent=4)
+            context["payload"] = json.dumps(payload, indent=4)
+            jwk = form_dict["jwk"]
+            if jwk:
                 verify_jws(jwt, jwk)
                 messages.success(request, _('Your jws is verified'))
-            except Exception:
-                messages.error(request, _("Jws verification failed"))
-                render(request, 'onboarding_decode_jwt.html', context)
-    return render(request, 'onboarding_decode_jwt.html', context)
+        except Exception as e:
+            messages.error(request, f"Jws verification failed: {e}")
+    return render(request, "onboarding_decode_jwt.html", context)
 
 
 def onboarding_apply_policy(request):
@@ -293,7 +291,7 @@ def onboarding_apply_policy(request):
                 "md": request.GET['md'],
                 "policy": request.GET['policy'],
                 "result": ""
-            }
+        }
         try:
             md = json.loads(request.GET['md'])
             policy = json.loads(request.GET['policy'])
diff --git a/spid_cie_oidc/provider/exceptions.py b/spid_cie_oidc/provider/exceptions.py
index af71c9ee..977f16d6 100644
--- a/spid_cie_oidc/provider/exceptions.py
+++ b/spid_cie_oidc/provider/exceptions.py
@@ -12,3 +12,7 @@ class RevokedSession(Exception):
 
 class ValidationException(Exception):
     pass
+
+
+class ExpiredAuthCode(Exception):
+    pass
\ No newline at end of file
diff --git a/spid_cie_oidc/provider/management/commands/fetch_openid_relying_parties.py b/spid_cie_oidc/provider/management/commands/fetch_openid_relying_parties.py
index 8782af95..3f6562e4 100644
--- a/spid_cie_oidc/provider/management/commands/fetch_openid_relying_parties.py
+++ b/spid_cie_oidc/provider/management/commands/fetch_openid_relying_parties.py
@@ -90,7 +90,7 @@ def handle(self, *args, **options):
                 tc = get_or_create_trust_chain(
                     subject=rp_sub,
                     trust_anchor=settings.OIDCFED_DEFAULT_TRUST_ANCHOR,
-                    metadata_type="openid_relying_party",
+                    metadata__relying_party__is_null=False,
                     httpc_params=settings.HTTPC_PARAMS,
                     required_trust_marks=getattr(
                         settings, "OIDCFED_REQUIRED_TRUST_MARKS", []
diff --git a/spid_cie_oidc/provider/models.py b/spid_cie_oidc/provider/models.py
index 926eeb6a..d40185f1 100644
--- a/spid_cie_oidc/provider/models.py
+++ b/spid_cie_oidc/provider/models.py
@@ -35,11 +35,16 @@ class OidcSession(TimeStampedModel):
 
     def set_sid(self, request):
         try:
-            # Session.objects.get(session_key=request.session.session_key)
+            Session.objects.get(session_key=request.session.session_key)
             self.sid = request.session.session_key
-            self.save()
         except Exception:
-            logger.warning(f"Error setting SID for OidcSession {self}")
+            logger.warning(
+                f"Error setting SID for OidcSession {self} "
+                "due to multiple authentication with different users with "
+                "the same browser."
+            )
+            self.sid = self.auth_code
+        self.save()
 
     def revoke(self, destroy_session=True):
         session = Session.objects.filter(session_key=self.sid)
diff --git a/spid_cie_oidc/provider/settings.py b/spid_cie_oidc/provider/settings.py
index 67a929a6..8ddc074b 100644
--- a/spid_cie_oidc/provider/settings.py
+++ b/spid_cie_oidc/provider/settings.py
@@ -6,8 +6,7 @@
     OPMetadataSpid
 )
 from spid_cie_oidc.onboarding.schemas.authn_requests import (
-    AcrValuesCie,
-    AcrValuesSpid,
+    AcrValues,
     AuthenticationRequestCie,
     AuthenticationRequestSpid
 )
@@ -117,11 +116,27 @@
     "OIDCFED_PROVIDER_AUTH_CODE_MAX_AGE",
     10
 )
-OIDCFED_PROVIDER_PROFILES_DEFAULT_ACR = dict(
-    spid = AcrValuesSpid.l2.value,
-    cie = AcrValuesCie.l2.value
+
+
+OIDCFED_PROVIDER_PROFILES_DEFAULT_ACR = getattr(
+    settings,
+    "OIDCFED_PROVIDER_PROFILES_DEFAULT_ACR",
+    dict(
+        spid = AcrValues.l2.value,
+        cie = AcrValues.l2.value
+    )
 )
 
+OIDCFED_PROVIDER_PROFILES_ACR_4_REFRESH = getattr(
+    settings,
+    "OIDCFED_PROVIDER_PROFILES_ACR_4_REFRESH",
+    dict(
+        spid = [AcrValues.l1.value],
+        cie = [AcrValues.l1.value]
+    )
+)
+
+
 OIDCFED_ATTRNAME_I18N = {
     # SPID
     "https://attributes.spid.gov.it/name": _("Name"),
diff --git a/spid_cie_oidc/provider/templates/op_user_history.html b/spid_cie_oidc/provider/templates/op_user_history.html
index 2735cf30..ef612944 100644
--- a/spid_cie_oidc/provider/templates/op_user_history.html
+++ b/spid_cie_oidc/provider/templates/op_user_history.html
@@ -6,10 +6,17 @@
 {% block menu_links %}
 
 <li class="nav-item ">
-    <a class="nav-link {% if request.resolver_match.view_name == 'oidc_provider_consent' %}active{% endif %}" 
-    href="{% url 'oidc_provider_consent' %}">
-        <span>{% trans 'Home' %}</span>
-    </a>
+    {% if user.is_staff %}
+        <a class="nav-link {% if request.resolver_match.view_name == 'oidc_provider_staff_testing' %}active{% endif %}" 
+        href="{% url 'oidc_provider_staff_testing' %}">
+            <span>{% trans 'Home' %}</span>
+        </a>
+    {% else %}
+        <a class="nav-link {% if request.resolver_match.view_name == 'oidc_provider_consent' %}active{% endif %}" 
+        href="{% url 'oidc_provider_consent' %}">
+            <span>{% trans 'Home' %}</span>
+        </a>
+    {% endif %}
 </li>
 <li class="nav-item">
     <a class="nav-link {% if request.resolver_match.view_name == 'oidc_provider_access_history' %}active{% endif %}" 
@@ -23,7 +30,7 @@
 {% block centered_container %}
 
 <div class="col-12">
-
+    
     <div class="row row-header border-bottom no-border-bottom-xs">
         <div class="wrap-container">
             <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
@@ -73,7 +80,7 @@ <h4 class="text-left">
                 <tr>
                     <th scope="row">{{ forloop.counter }}</th>
                     <td>{{ session.client_id }}</td>
-                    <td>{{ session.sid}}</td>
+                    <td>{{ session.sid|truncatechars:33 }}</td>
                     <td>{{ session.created | date:'Y-m-d H:i'}}</td>
                     {% if session.revoked %}
                         <td class="text-success">
diff --git a/spid_cie_oidc/provider/views/__init__.py b/spid_cie_oidc/provider/views/__init__.py
index a0414d06..6603f22c 100644
--- a/spid_cie_oidc/provider/views/__init__.py
+++ b/spid_cie_oidc/provider/views/__init__.py
@@ -11,7 +11,7 @@
 from spid_cie_oidc.entity.settings import HTTPC_PARAMS
 from spid_cie_oidc.entity.trust_chain_operations import get_or_create_trust_chain
 from spid_cie_oidc.entity.utils import datetime_from_timestamp, exp_from_now, iat_now
-from spid_cie_oidc.provider.exceptions import AuthzRequestReplay, InvalidSession, RevokedSession, ValidationException
+from spid_cie_oidc.provider.exceptions import AuthzRequestReplay, ExpiredAuthCode, InvalidSession, RevokedSession, ValidationException
 from spid_cie_oidc.provider.models import OidcSession
 
 from spid_cie_oidc.provider.settings import (
@@ -19,6 +19,8 @@
     OIDCFED_DEFAULT_PROVIDER_PROFILE,
     OIDCFED_PROVIDER_AUTH_CODE_MAX_AGE,
     OIDCFED_PROVIDER_PROFILES,
+    OIDCFED_PROVIDER_PROFILES_ACR_4_REFRESH,
+    OIDCFED_PROVIDER_PROFILES_DEFAULT_ACR,
     OIDCFED_PROVIDER_PROFILES_ID_TOKEN_CLAIMS
 )
 logger = logging.getLogger(__name__)
@@ -54,7 +56,7 @@ def validate_authz_request_object(self, req) -> TrustChain:
 
         self.is_a_replay_authz()
         rp_trust_chain = TrustChain.objects.filter(
-            type="openid_relying_party",
+            metadata__openid_relying_party__isnull=False,
             sub=self.payload["iss"],
             trust_anchor__sub=settings.OIDCFED_DEFAULT_TRUST_ANCHOR
         ).first()
@@ -71,7 +73,6 @@ def validate_authz_request_object(self, req) -> TrustChain:
             rp_trust_chain = get_or_create_trust_chain(
                 subject=self.payload["iss"],
                 trust_anchor=settings.OIDCFED_DEFAULT_TRUST_ANCHOR,
-                metadata_type="openid_relying_party",
                 httpc_params=HTTPC_PARAMS,
                 required_trust_marks=getattr(
                     settings, "OIDCFED_REQUIRED_TRUST_MARKS", []
@@ -87,7 +88,7 @@ def validate_authz_request_object(self, req) -> TrustChain:
                 )
                 raise Exception()
 
-        jwks = rp_trust_chain.metadata["jwks"]["keys"]
+        jwks = rp_trust_chain.metadata['openid_relying_party']["jwks"]["keys"]
         jwk = self.find_jwk(header, jwks)
         if not jwk:
             state = self.payload.get("state", "")
@@ -134,19 +135,30 @@ def check_session(self, request) -> OidcSession:
             raise InvalidSession()
 
         session = OidcSession.objects.filter(
-            auth_code=request.session["oidc"]["auth_code"],
-            user=request.user,
-            created__lte = timezone.localtime() + timezone.timedelta(
-                minutes = OIDCFED_PROVIDER_AUTH_CODE_MAX_AGE
-            )
+            auth_code=request.session["oidc"]["auth_code"]
         ).first()
 
         if not session:
-            raise InvalidSession()
-
+            raise InvalidSession("Unknown Session")
+        
+        if session.user != request.user:
+            raise InvalidSession(
+                f"Different user, session is of {session.user} "
+                f"but request user is {request.user}"
+            )
+        
         if session.revoked:
-            raise RevokedSession()
-
+            raise RevokedSession(
+                "Session was revoked"
+            )
+        
+        session_not_after = session.created + timezone.timedelta(
+            minutes = OIDCFED_PROVIDER_AUTH_CODE_MAX_AGE
+        )
+        if session_not_after < timezone.localtime():
+            raise ExpiredAuthCode(
+                f"The Session is expired at {session_not_after}"
+            )
         return session
 
     def get_issuer(self):
@@ -157,14 +169,17 @@ def get_issuer(self):
     def check_client_assertion(self, client_id: str, client_assertion: str) -> bool:
         head = unpad_jwt_head(client_assertion)
         payload = unpad_jwt_payload(client_assertion)
-        if payload['sub'] != client_id:
+        _sub = payload.get('sub', None)
+        if _sub != client_id:
+            logger.warning(
+                f"Client assertion failed: {_sub} != {client_id}"
+            )
             # TODO Specialize exceptions
             raise Exception()
 
         tc = TrustChain.objects.get(sub=client_id, is_active=True)
-        jwk = self.find_jwk(head, tc.metadata['jwks']['keys'])
+        jwk = self.find_jwk(head, tc.metadata['openid_relying_party']['jwks']['keys'])
         verify_jws(client_assertion, jwk)
-
         return True
 
     def validate_json_schema(self, payload, schema_type, error_description):
@@ -253,9 +268,12 @@ def get_refresh_token(
             commons:dict
     ) -> dict:
         # refresh token is scope offline_access and prompt == consent
+        refresh_acrs = OIDCFED_PROVIDER_PROFILES_ACR_4_REFRESH[OIDCFED_DEFAULT_PROVIDER_PROFILE]
+        acrs = authz.authz_request.get('acr_values', [])
         if (
             "offline_access" in authz.authz_request['scope'] and
-            'consent' in authz.authz_request['prompt']
+            'consent' in authz.authz_request['prompt'] and 
+            set(refresh_acrs).intersection(set(acrs))
         ):
             refresh_token = {
                 "sub": sub,
diff --git a/spid_cie_oidc/provider/views/authz_request_view.py b/spid_cie_oidc/provider/views/authz_request_view.py
index 4ed93e35..13c7de64 100644
--- a/spid_cie_oidc/provider/views/authz_request_view.py
+++ b/spid_cie_oidc/provider/views/authz_request_view.py
@@ -31,7 +31,6 @@ class AuthzRequestView(OpBase, View):
         View which processes the actual Authz request and
         returns a Http Redirect
     """
-
     template = "op_user_login.html"
 
     def validate_authz(self, payload: dict):
@@ -44,6 +43,14 @@ def validate_authz(self, payload: dict):
                 else:
                     payload[i] = [payload[i]]
 
+        if (
+            'offline_access' in payload['scope'] and
+            'consent' not in payload['prompt'] 
+        ):
+            raise ValidationError(
+                "scope with offline_access without prompt = consent"
+            )
+
         redirect_uri = payload.get("redirect_uri", "")
         p = urllib.parse.urlparse(redirect_uri)
         scheme_fqdn = f"{p.scheme}://{p.hostname}"
@@ -172,6 +179,7 @@ def post(self, request, *args, **kwargs):
         username = form.cleaned_data.get("username")
         password = form.cleaned_data.get("password")
         user = authenticate(username=username, password=password)
+
         if not user:
             errors = form._errors.setdefault("username", ErrorList())
             errors.append(_("invalid username or password"))
diff --git a/spid_cie_oidc/provider/views/consent_page_view.py b/spid_cie_oidc/provider/views/consent_page_view.py
index 1d0e0479..d349fa6a 100644
--- a/spid_cie_oidc/provider/views/consent_page_view.py
+++ b/spid_cie_oidc/provider/views/consent_page_view.py
@@ -8,7 +8,6 @@
     HttpResponseRedirect,
 )
 from django.shortcuts import redirect, render
-from django.urls import reverse
 from django.utils.translation import gettext as _
 from django.views import View
 from spid_cie_oidc.entity.models import (
@@ -39,7 +38,7 @@ def get(self, request, *args, **kwargs):
 
         tc = TrustChain.objects.filter(
             sub=session.client_id,
-            type="openid_relying_party",
+            metadata__openid_relying_party__isnull=False,
             is_active = True
         ).first()
 
@@ -66,8 +65,8 @@ def get(self, request, *args, **kwargs):
     def post(self, request, *args, **kwargs):
         try:
             session = self.check_session(request)
-        except Exception:
-            logger.warning("Invalid session")
+        except Exception as e:
+            logger.warning(f"Invalid session: {e}")
             return HttpResponseForbidden()
 
         self.payload = session.authz_request
diff --git a/spid_cie_oidc/provider/views/token_endpoint.py b/spid_cie_oidc/provider/views/token_endpoint.py
index 5695d8af..574ca0b7 100644
--- a/spid_cie_oidc/provider/views/token_endpoint.py
+++ b/spid_cie_oidc/provider/views/token_endpoint.py
@@ -32,9 +32,13 @@ def grant_auth_code(self, request, *args, **kwargs):
         """
         # PKCE check - based on authz.authz_request["code_challenge_method"] == S256
         code_challenge = hashlib.sha256(request.POST["code_verifier"].encode()).digest()
-        code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8")
-        code_challenge = code_challenge.replace("=", "")
-        if code_challenge != self.authz.authz_request["code_challenge"]:
+        code_challenge_b64 = base64.urlsafe_b64encode(code_challenge).decode("utf-8")
+        code_challenge_unpad = code_challenge_b64.replace("=", "")
+        if code_challenge_unpad != self.authz.authz_request["code_challenge"]:
+            logger.warning(
+                f"PCKE validation failed. Produced code challenge [{code_challenge_unpad}]"
+                f" if different from which sent by RP [{self.authz.authz_request['code_challenge']}]"
+            )
             return HttpResponseForbidden()
         #
 
@@ -160,8 +164,12 @@ def post(self, request, *args, **kwargs):
                 request.POST['client_id'],
                 request.POST['client_assertion']
             )
-        except Exception:
+        except Exception as e:
             # TODO: coverage test
+            logger.warning(
+                "Client authentication failed for "
+                f"{request.POST.get('client_id', 'unknow')}: {e}"
+            )
             return JsonResponse(
                 # TODO: error message here
                 {
diff --git a/spid_cie_oidc/provider/views/userinfo_endpoint.py b/spid_cie_oidc/provider/views/userinfo_endpoint.py
index a5cbe295..5785196a 100644
--- a/spid_cie_oidc/provider/views/userinfo_endpoint.py
+++ b/spid_cie_oidc/provider/views/userinfo_endpoint.py
@@ -40,7 +40,7 @@ def get(self, request, *args, **kwargs):
 
         rp_tc = TrustChain.objects.filter(
             sub=token.session.client_id,
-            type="openid_relying_party",
+            metadata__openid_relying_party__isnull=False,
             is_active=True
         ).first()
         if not rp_tc:
@@ -63,5 +63,5 @@ def get(self, request, *args, **kwargs):
         jws = create_jws(jwt, issuer.jwks[0])
 
         # encrypt the data
-        jwe = encrypt_dict(jws, rp_tc.metadata["jwks"]["keys"][0])
+        jwe = encrypt_dict(jws, rp_tc.metadata['openid_relying_party']["jwks"]["keys"][0])
         return HttpResponse(jwe, content_type="application/jose")
diff --git a/spid_cie_oidc/relying_party/management/commands/fetch_openid_providers.py b/spid_cie_oidc/relying_party/management/commands/fetch_openid_providers.py
index abe91a90..97d763d8 100644
--- a/spid_cie_oidc/relying_party/management/commands/fetch_openid_providers.py
+++ b/spid_cie_oidc/relying_party/management/commands/fetch_openid_providers.py
@@ -48,7 +48,6 @@ def handle(self, *args, **options):
                     tc = get_or_create_trust_chain(
                         subject=op_sub,
                         trust_anchor=ta,
-                        metadata_type="openid_provider",
                         httpc_params=HTTPC_PARAMS,
                         required_trust_marks=getattr(
                             settings, "OIDCFED_REQUIRED_TRUST_MARKS", []
diff --git a/spid_cie_oidc/relying_party/settings.py b/spid_cie_oidc/relying_party/settings.py
index fbb5ad1b..59ed1122 100644
--- a/spid_cie_oidc/relying_party/settings.py
+++ b/spid_cie_oidc/relying_party/settings.py
@@ -1,5 +1,5 @@
 from django.conf import settings
-from spid_cie_oidc.onboarding.schemas.authn_requests import AcrValuesCie, AcrValuesSpid, AuthenticationRequestCie, AuthenticationRequestSpid
+from spid_cie_oidc.onboarding.schemas.authn_requests import AcrValues, AuthenticationRequestCie, AuthenticationRequestSpid
 from spid_cie_oidc.entity.schemas.rp_metadata import RPMetadataSpid, RPMetadataCie
 from spid_cie_oidc.onboarding.schemas.authn_response import AuthenticationResponse, AuthenticationResponseCie
 from spid_cie_oidc.onboarding.schemas.token_response import TokenResponse
@@ -18,10 +18,7 @@
 OIDCFED_ACR_PROFILES = getattr(
     settings,
     "OIDCFED_ACR_PROFILES",
-    dict(
-        spid = AcrValuesSpid.l2.value,
-        cie = AcrValuesCie.l2.value
-     )
+    AcrValues.l2.value
 )
 
 RP_PROVIDER_PROFILES = getattr(
diff --git a/spid_cie_oidc/relying_party/static/css/access-button.css b/spid_cie_oidc/relying_party/static/css/access-button.css
index 085c12df..bcefaa66 100644
--- a/spid_cie_oidc/relying_party/static/css/access-button.css
+++ b/spid_cie_oidc/relying_party/static/css/access-button.css
@@ -403,7 +403,7 @@ svg {
     height: 35px;
     padding-bottom: 5px;
     border: 0;
-    width: 100%;
+    width: 30%;
 }
 
 
diff --git a/spid_cie_oidc/relying_party/templates/rp_landing.html b/spid_cie_oidc/relying_party/templates/rp_landing.html
index ab358ef2..dc2a6d51 100644
--- a/spid_cie_oidc/relying_party/templates/rp_landing.html
+++ b/spid_cie_oidc/relying_party/templates/rp_landing.html
@@ -54,9 +54,9 @@ <h3 class="text-left">
                         {% for provider in spid_providers %}
                             <li>
                                 <a href="{% url 'spid_cie_rp_begin' %}?provider={{provider.sub}}&profile=spid">
-                                    <span class="spid-sr-only">{{ provider.metadata.organization_name }}</span>
-                                    {% if provider.metadata.logo_uri %}
-                                    <img src="{{ provider.metadata.logo_uri }}" alt="{{ provider.metadata.organization_name }}">
+                                    <span class="spid-sr-only">{{ provider.metadata.openid_provider.organization_name }}</span>
+                                    {% if provider.metadata.openid_provider.logo_uri %}
+                                    <img src="{{ provider.metadata.openid_provider.logo_uri }}" alt="{{ provider.metadata.organization_name }}">
                                     {% else %}
                                     <img src="{% static 'svg/no_image_available.svg' %}" alt="{{ provider.metadata.organization_name }}">
                                     {% endif %}
@@ -67,7 +67,7 @@ <h3 class="text-left">
                 </div>
 
             </span>
-            
+
             <span
                 class="badge square-corners mb-3 mr-2 ml-0 pr-10 p-2 mw-100"
                 style="font-size: 100%">
@@ -88,9 +88,9 @@ <h3 class="text-left">
                         {% for provider in cie_providers %}
                             <li>
                                 <a href="{% url 'spid_cie_rp_begin' %}?provider={{provider.sub}}&profile=cie">
-                                    <span class="spid-sr-only">{{ provider.metadata.organization_name }}</span>
-                                    {% if provider.metadata.logo_uri %}
-                                    <img src="{{ provider.metadata.logo_uri }}" alt="{{ provider.metadata.organization_name }}">
+                                    <span class="spid-sr-only">{{ provider.metadata.openid_provider.organization_name }}</span>
+                                    {% if provider.metadata.openid_provider.logo_uri %}
+                                    <img src="{{ provider.metadata.openid_provider.logo_uri }}" alt="{{ provider.metadata.organization_name }}">
                                     {% else %}
                                     <img src="{% static 'svg/no_image_available.svg' %}" alt="{{ provider.metadata.organization_name }}">
                                     {% endif %}
diff --git a/spid_cie_oidc/relying_party/urls.py b/spid_cie_oidc/relying_party/urls.py
index ca10ab00..bb63fcde 100644
--- a/spid_cie_oidc/relying_party/urls.py
+++ b/spid_cie_oidc/relying_party/urls.py
@@ -1,12 +1,10 @@
 from django.urls import path
 
-from .views import (
-    oidc_rp_landing,
-    oidc_rpinitiated_logout,
-    SpidCieOidcRpBeginView,
-    SpidCieOidcRpCallbackEchoAttributes,
-    SpidCieOidcRpCallbackView,
-)
+from .views.rp_begin import SpidCieOidcRpBeginView
+from .views.rp_callback import SpidCieOidcRpCallbackView
+from .views.rp_callback_echo_attributes import SpidCieOidcRpCallbackEchoAttributes
+from .views.rp_initiated_logout import oidc_rpinitiated_logout
+from .views.rp_landing import oidc_rp_landing
 
 urlpatterns = []
 urlpatterns += (
diff --git a/spid_cie_oidc/relying_party/views.py b/spid_cie_oidc/relying_party/views.py
deleted file mode 100644
index 555666d2..00000000
--- a/spid_cie_oidc/relying_party/views.py
+++ /dev/null
@@ -1,607 +0,0 @@
-import json
-import logging
-from copy import deepcopy
-import random
-
-import requests
-from django.conf import settings
-from django.contrib.auth import get_user_model, login, logout
-from django.contrib.auth.decorators import login_required
-from django.http import HttpResponseRedirect, JsonResponse
-from django.shortcuts import render
-from django.urls import reverse
-from django.utils import timezone
-from django.utils.module_loading import import_string
-from django.utils.translation import gettext as _
-from django.views import View
-from pydantic import ValidationError
-from spid_cie_oidc.entity.exceptions import InvalidTrustchain
-from spid_cie_oidc.entity.jwtse import (
-    create_jws,
-    unpad_jwt_head,
-    unpad_jwt_payload,
-    verify_jws
-)
-from spid_cie_oidc.entity.models import (FederationEntityConfiguration,
-                                         TrustChain)
-from spid_cie_oidc.entity.settings import HTTPC_PARAMS
-from spid_cie_oidc.entity.statements import get_http_url
-from spid_cie_oidc.entity.trust_chain_operations import get_or_create_trust_chain
-from spid_cie_oidc.relying_party.exceptions import ValidationException
-from spid_cie_oidc.relying_party.settings import (
-    RP_DEFAULT_PROVIDER_PROFILES,
-    RP_PROVIDER_PROFILES,
-    OIDCFED_ACR_PROFILES
-)
-
-from .models import OidcAuthentication, OidcAuthenticationToken
-from .oauth2 import *
-from .oidc import *
-from .settings import (
-    RP_ATTR_MAP,
-    RP_PKCE_CONF,
-    RP_REQUEST_CLAIM_BY_PROFILE,
-    RP_USER_CREATE,
-    RP_USER_LOOKUP_FIELD,
-)
-from .utils import (
-    http_dict_to_redirect_uri_path,
-    process_user_attributes,
-    random_string,
-)
-
-logger = logging.getLogger(__name__)
-
-
-class SpidCieOidcRp:
-    """
-    Baseclass with common methods for RPs
-    """
-
-    def get_jwks_from_jwks_uri(self, jwks_uri: str) -> dict:
-        """
-        get jwks
-        """
-        try:
-            jwks_dict = get_http_url([jwks_uri], httpc_params=HTTPC_PARAMS).json()
-        except Exception as e:
-            logger.error(f"Failed to download jwks from {jwks_uri}: {e}")
-            return {}
-        return jwks_dict
-
-    def get_oidc_op(self, request) -> TrustChain:
-        """
-            get available trust to a specific OP
-        """
-        if not request.GET.get("provider", None):
-            logger.warning(
-                "Missing provider url. Please try '?provider=https://provider-subject/'"
-            )
-            raise InvalidTrustchain(
-                "Missing provider url. Please try '?provider=https://provider-subject/'"
-            )
-        trust_anchor = request.GET.get(
-            "trust_anchor",
-            settings.OIDCFED_IDENTITY_PROVIDERS.get(
-                request.GET["provider"],
-                settings.OIDCFED_DEFAULT_TRUST_ANCHOR
-            )
-        )
-
-        if trust_anchor not in settings.OIDCFED_TRUST_ANCHORS:
-            logger.warning("Unallowed Trust Anchor")
-            raise InvalidTrustchain("Unallowed Trust Anchor")
-
-        tc = TrustChain.objects.filter(
-            sub=request.GET["provider"],
-            trust_anchor__sub=trust_anchor,
-        ).first()
-
-        discover_trust = False
-        if not tc:
-            logger.info(f'Trust Chain not found for {request.GET["provider"]}')
-            discover_trust = True
-
-        elif not tc.is_active:
-            logger.warning(f"{tc} found but DISABLED at {tc.modified}")
-            raise InvalidTrustchain(f"{tc} found but DISABLED at {tc.modified}")
-
-        elif tc.is_expired:
-            logger.warning(f"{tc} found but expired at {tc.exp}")
-            logger.warning("Try to renew the trust chain")
-            discover_trust = True
-
-        if discover_trust:
-            tc = get_or_create_trust_chain(
-                subject=request.GET["provider"],
-                trust_anchor=trust_anchor,
-                # TODO - not sure that it's required for a RP that fetches OP directly from TA
-                # required_trust_marks = [],
-                metadata_type="openid_provider",
-                force=True,
-            )
-        return tc
-
-    def validate_json_schema(self, request, schema_type, error_description):
-        try:
-            schema = RP_PROVIDER_PROFILES[RP_DEFAULT_PROVIDER_PROFILES]
-            schema[schema_type](**request)
-        except ValidationError as e:
-            logger.error(
-                f"{error_description} "
-                f"for {request.get('client_id', None)}: {e}"
-            )
-            raise ValidationException()
-
-
-class SpidCieOidcRpBeginView(SpidCieOidcRp, View):
-    """
-        View which processes the actual Authz request and
-        returns a Http Redirect
-    """
-
-    error_template = "rp_error.html"
-
-    def get(self, request, *args, **kwargs):
-        """
-        http://localhost:8001/oidc/rp/authorization?
-        provider=http://127.0.0.1:8002/
-        """
-        try:
-            tc = self.get_oidc_op(request)
-            if not tc:
-                context = {
-                    "error": "request rejected",
-                    "error_description": "Trust Chain is unavailable.",
-                }
-                return render(request, self.error_template, context)
-
-        except InvalidTrustchain as exc:
-            context = {
-                "error": "request rejected",
-                "error_description": str(exc.args),
-            }
-            return render(request, self.error_template, context)
-
-        except Exception as exc:
-            context = {
-                "error": "request rejected",
-                "error_description": _(str(exc.args)),
-            }
-            return render(request, self.error_template, context)
-
-        provider_metadata = tc.metadata
-        if not provider_metadata:
-            context = {
-                "error": "request rejected",
-                "error_description": _("provider metadata not found"),
-            }
-            return render(request, self.error_template, context)
-
-        entity_conf = FederationEntityConfiguration.objects.filter(
-            entity_type="openid_relying_party",
-            # TODO: RPs multitenancy?
-            # sub = request.build_absolute_uri()
-        ).first()
-
-        if not entity_conf:
-            context = {
-                "error": "request rejected",
-                "error_description": _("Missing configuration."),
-            }
-            return render(request, self.error_template, context)
-
-        client_conf = entity_conf.metadata["openid_relying_party"]
-        if not (
-            provider_metadata.get("jwks_uri", None)
-            or provider_metadata.get("jwks", None)
-        ):
-            context = {
-                "error": "request rejected",
-                "error_description": _("Invalid provider Metadata."),
-            }
-            return render(request, self.error_template, context)
-
-        if provider_metadata.get("jwks", None):
-            jwks_dict = provider_metadata["jwks"]
-        else:
-            jwks_dict = self.get_jwks_from_jwks_uri(provider_metadata["jwks_uri"])
-        if not jwks_dict:
-            _msg = f"Failed to get jwks from {tc.sub}"
-            logger.error(_msg)
-            context = {
-                "error": "request rejected",
-                "error_description": _msg
-            }
-            return render(request, self.error_template, context)
-
-        authz_endpoint = provider_metadata["authorization_endpoint"]
-
-        redirect_uri = request.GET.get("redirect_uri", client_conf["redirect_uris"][0])
-        if redirect_uri not in client_conf["redirect_uris"]:
-            logger.warning(
-                f"Requested for unknown redirect uri {redirect_uri}. "
-                f"Reverted to default {client_conf['redirect_uris'][0]}."
-            )
-            redirect_uri = client_conf["redirect_uris"][0]
-        _profile = request.GET.get("profile", "spid")
-        _acr = OIDCFED_ACR_PROFILES[_profile]
-        authz_data = dict(
-            scope= request.GET.get("scope", None) or "openid",
-            redirect_uri=redirect_uri,
-            response_type=client_conf["response_types"][0],
-            nonce=random_string(32),
-            state=random_string(32),
-            client_id=client_conf["client_id"],
-            endpoint=authz_endpoint,
-            acr_values= _acr,
-            iat=int(timezone.localtime().timestamp()),
-            aud=[tc.sub, authz_endpoint],
-            claims=RP_REQUEST_CLAIM_BY_PROFILE[_profile],
-        )
-
-        _prompt = request.GET.get("prompt", "consent login")
-
-        # if "offline_access" in authz_data["scope"]:
-        # _prompt.extend(["consent login"])
-
-        authz_data["prompt"] = _prompt
-
-        # PKCE
-        pkce_func = import_string(RP_PKCE_CONF["function"])
-        pkce_values = pkce_func(**RP_PKCE_CONF["kwargs"])
-        authz_data.update(pkce_values)
-        #
-        authz_entry = dict(
-            client_id=client_conf["client_id"],
-            state=authz_data["state"],
-            endpoint=authz_endpoint,
-            # TODO: better have here an organization name
-            provider=tc.sub,
-            provider_id=tc.sub,
-            data=json.dumps(authz_data),
-            provider_jwks=json.dumps(jwks_dict),
-            provider_configuration=provider_metadata,
-        )
-
-        # TODO: Prune the old or unbounded authz ...
-        OidcAuthentication.objects.create(**authz_entry)
-
-        authz_data.pop("code_verifier")
-        # add the signed request object
-        authz_data_obj = deepcopy(authz_data)
-        authz_data_obj["iss"] = client_conf["client_id"]
-        authz_data_obj["sub"] = client_conf["client_id"]
-
-        request_obj = create_jws(authz_data_obj, entity_conf.jwks[0])
-        authz_data["request"] = request_obj
-        uri_path = http_dict_to_redirect_uri_path(authz_data)
-        url = "?".join((authz_endpoint, uri_path))
-        logger.info(f"Starting Authz request to {url}")
-        return HttpResponseRedirect(url)
-
-
-class SpidCieOidcRpCallbackView(View, SpidCieOidcRp, OidcUserInfo, OAuth2AuthorizationCodeGrant):
-    """
-        View which processes an Authorization Response
-        https://tools.ietf.org/html/rfc6749#section-4.1.2
-
-        eg:
-        /redirect_uri?code=tYkP854StRqBVcW4Kg4sQfEN5Qz&state=R9EVqaazGsj3wg5JgxIgm8e8U4BMvf7W
-    """
-
-    error_template = "rp_error.html"
-
-    def user_reunification(self, user_attrs: dict):
-        user_model = get_user_model()
-        lookup = {
-            f"attributes__{RP_USER_LOOKUP_FIELD}": user_attrs[RP_USER_LOOKUP_FIELD]
-        }
-        user = user_model.objects.filter(**lookup).first()
-        if user:
-            user.attributes.update(user_attrs)
-            user.save()
-            logger.info(f"{RP_USER_LOOKUP_FIELD} matched on user {user}")
-            return user
-        elif RP_USER_CREATE:
-            user = user_model.objects.create(
-                username=user_attrs.get("username", user_attrs["sub"]),
-                first_name=user_attrs.get("given_name", user_attrs["sub"]),
-                last_name=user_attrs.get("family_name", user_attrs["sub"]),
-                email=user_attrs.get("email", ""),
-                attributes=user_attrs,
-            )
-            logger.info(f"Created new user {user}")
-            return user
-
-    def get_jwk_from_jwt(self, jwt: str, provider_jwks: dict) -> dict:
-        """
-            docs here
-        """
-        head = unpad_jwt_head(jwt)
-        kid = head["kid"]
-        for jwk in provider_jwks:
-            if jwk["kid"] == kid:
-                return jwk
-        return {}
-
-    def get(self, request, *args, **kwargs):
-        """
-            The Authorization callback, the redirect uri where the auth code lands
-        """
-        request_args = {k: v for k, v in request.GET.items()}
-        if "error" in request_args:
-            return render(
-                request,
-                self.error_template,
-                request_args,
-                status=401
-            )
-        authz = OidcAuthentication.objects.filter(
-            state=request_args.get("state"),
-        )
-        try:
-            self.validate_json_schema(
-                request.GET.dict(),
-                "authn_response",
-                "Authn response object validation failed"
-            )
-        except ValidationException:
-            return JsonResponse(
-                {
-                    "error": "invalid_request",
-                    "error_description": "Authn response object validation failed",
-                },
-                status = 400
-            )
-
-        if not authz:
-            # TODO: verify error message and status
-            context = {
-                "error": "unauthorized request",
-                "error_description": _("Authentication not found"),
-            }
-            return render(request, self.error_template, context, status=401)
-        else:
-            authz = authz.last()
-
-        code = request.GET.get("code")
-        if not code:
-            # TODO: verify error message and status
-            context = {
-                "error": "invalid request",
-                "error_description": _("Request MUST contain code"),
-            }
-            return render(request, self.error_template, context, status=400)
-
-        authz_token = OidcAuthenticationToken.objects.create(
-            authz_request=authz, code=code
-        )
-        self.rp_conf = FederationEntityConfiguration.objects.get(
-            sub=authz_token.authz_request.client_id
-        )
-        if not self.rp_conf:
-            # TODO: verify error message and status
-            context = {
-                "error": "invalid request",
-                "error_description": _("Relay party not found"),
-            }
-            return render(request, self.error_template, context, status=400)
-
-        authz_data = json.loads(authz.data)
-        token_response = self.access_token_request(
-            redirect_uri=authz_data["redirect_uri"],
-            state=authz.state,
-            code=code,
-            issuer_id=authz.provider_id,
-            client_conf=self.rp_conf,
-            token_endpoint_url=authz.provider_configuration["token_endpoint"],
-            audience=[authz.provider_id],
-            code_verifier=authz_data.get("code_verifier"),
-        )
-        if not token_response:
-            # TODO: verify error message
-            context = {
-                "error": "invalid token response",
-                "error_description": _("Token response seems not to be valid"),
-            }
-            return render(request, self.error_template, context, status=400)
-
-        else:
-            try:
-                self.validate_json_schema(
-                    token_response,
-                    "token_response",
-                    "Token response object validation failed"
-                )
-            except ValidationException:
-                return JsonResponse(
-                    {
-                        "error": "invalid_request",
-                        "error_description": "Token response object validation failed",
-                    },
-                    status = 400
-                )
-        jwks = authz.provider_configuration["jwks"]["keys"]
-        access_token = token_response["access_token"]
-        id_token = token_response["id_token"]
-        op_ac_jwk = self.get_jwk_from_jwt(access_token, jwks)
-        op_id_jwk = self.get_jwk_from_jwt(id_token, jwks)
-
-        if not op_ac_jwk or not op_id_jwk:
-            # TODO: verify error message and status
-            context = {
-                "error": "invalid token",
-                "error_description": _("Authentication token seems not to be valid."),
-            }
-            return render(request, self.error_template, context, status=403)
-
-        try:
-            verify_jws(access_token, op_ac_jwk)
-        except Exception:
-            # TODO: verify error message
-            context = {
-                "error": "token verification failed",
-                "error_description": _("Authentication token validation error."),
-            }
-            return render(request, self.error_template, context, status=403)
-
-        try:
-            verify_jws(id_token, op_id_jwk)
-        except Exception:
-            # TODO: verify error message
-            context = {
-                "error": "token verification failed",
-                "error_description": _("ID token validation error."),
-            }
-            return render(request, self.error_template, context, status=403)
-
-        decoded_id_token = unpad_jwt_payload(id_token)
-        logger.debug(decoded_id_token)
-
-        decoded_access_token = unpad_jwt_payload(access_token)
-        logger.debug(decoded_access_token)
-
-        authz_token.access_token = access_token
-        authz_token.id_token = id_token
-        authz_token.scope = token_response.get("scope")
-        authz_token.token_type = token_response["token_type"]
-        authz_token.expires_in = token_response["expires_in"]
-        authz_token.save()
-        userinfo = self.get_userinfo(
-            authz.state,
-            authz_token.access_token,
-            authz.provider_configuration,
-            verify=HTTPC_PARAMS,
-        )
-        if not userinfo:
-            # TODO: verify error message
-            context = {
-                "error": "invalid userinfo response",
-                "error_description": _("UserInfo response seems not to be valid"),
-            }
-            return render(request, self.error_template, context, status=400)
-
-        # here django user attr mapping
-        user_attrs = process_user_attributes(userinfo, RP_ATTR_MAP, authz.__dict__)
-        if not user_attrs:
-            _msg = "No user attributes have been processed"
-            logger.warning(f"{_msg}: {userinfo}")
-            # TODO: verify error message and status
-            context = {
-                "error": "missing user attributes",
-                "error_description": _(f"{_msg}: {userinfo}"),
-            }
-            return render(request, self.error_template, context, status=403)
-
-        user = self.user_reunification(user_attrs)
-        if not user:
-            # TODO: verify error message and status
-            context = {"error": _("No user found"), "error_description": _("")}
-            return render(request, self.error_template, context, status=403)
-
-        # authenticate the user
-        login(request, user)
-        request.session["oidc_rp_user_attrs"] = user_attrs
-        authz_token.user = user
-        authz_token.save()
-        return HttpResponseRedirect(
-            getattr(
-                settings, "LOGIN_REDIRECT_URL", None
-            ) or reverse("spid_cie_rp_echo_attributes")
-        )
-
-
-class SpidCieOidcRpCallbackEchoAttributes(View):
-    template = "echo_attributes.html"
-
-    def get(self, request):
-        data = {"oidc_rp_user_attrs": request.session.get("oidc_rp_user_attrs", {})}
-        return render(request, self.template, data)
-
-
-@login_required
-def oidc_rpinitiated_logout(request):
-    """
-        Call the token revocation endpoint of the op
-    """
-    auth_tokens = OidcAuthenticationToken.objects.filter(
-        user=request.user
-    ).filter(revoked__isnull=True)
-
-    default_logout_url = getattr(
-        settings, "LOGOUT_REDIRECT_URL", None
-    ) or reverse("spid_cie_rp_landing")
-    if not auth_tokens:
-        logger.warning(
-            "Token revocation failed: not found any authentication session"
-        )
-        return HttpResponseRedirect(default_logout_url)
-
-    auth_token = auth_tokens.last()
-    authz = auth_token.authz_request
-    provider_conf = authz.provider_configuration
-    revocation_endpoint_url = provider_conf.get("revocation_endpoint")
-
-    # first of all on RP side ...
-    logger.info(f"{request.user} logout")
-    logout(request)
-    if not revocation_endpoint_url:
-        logger.warning(
-            f"{authz.provider_id} doesn't expose the token revocation endpoint."
-        )
-        return HttpResponseRedirect(default_logout_url)
-    else:
-        rp_conf = FederationEntityConfiguration.objects.filter(
-            sub= authz.client_id,
-            is_active=True,
-        ).first()
-
-        # private_key_jwt
-        client_assertion = create_jws(
-            {
-                "iss": authz.client_id,
-                "sub": authz.client_id,
-                "aud": [revocation_endpoint_url],
-                "iat": iat_now(),
-                "exp": exp_from_now(),
-                "jti": str(uuid.uuid4()),
-            },
-            jwk_dict=rp_conf.jwks[0],
-        )
-
-        auth_token.logged_out = timezone.localtime()
-        auth_token.save()
-
-        revocation_request = dict(
-            token = auth_token.access_token,
-            client_id = authz.client_id,
-            client_assertion = client_assertion,
-            client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
-        )
-        try:
-            requests.post(revocation_endpoint_url, data = revocation_request)
-        except Exception as e:
-            logger.warning(f"Token revocation failed: {e}")
-        auth_tokens.update(revoked = timezone.localtime())
-        return HttpResponseRedirect(default_logout_url)
-
-
-def oidc_rp_landing(request):
-    trust_chains = TrustChain.objects.filter(
-        type="openid_provider", is_active=True
-    )
-    spid_providers = []
-    cie_providers = []
-    for tc in trust_chains:
-        if tc.is_active:
-            if tc.sub in settings.OIDCFED_IDENTITY_PROVIDERS.get("spid", []):
-                spid_providers.append(tc)
-            elif tc.sub in settings.OIDCFED_IDENTITY_PROVIDERS.get("cie", []):
-                cie_providers.append(tc)
-    random.shuffle(spid_providers)
-    content = {
-        "spid_providers": spid_providers,
-        "cie_providers" : cie_providers
-    }
-    return render(request, "rp_landing.html", content)
diff --git a/spid_cie_oidc/relying_party/views/__init__.py b/spid_cie_oidc/relying_party/views/__init__.py
new file mode 100644
index 00000000..8e8c4e64
--- /dev/null
+++ b/spid_cie_oidc/relying_party/views/__init__.py
@@ -0,0 +1,95 @@
+import logging
+from django.conf import settings
+from pydantic import ValidationError
+from spid_cie_oidc.entity.exceptions import InvalidTrustchain
+from spid_cie_oidc.entity.models import TrustChain
+from spid_cie_oidc.entity.settings import HTTPC_PARAMS
+from spid_cie_oidc.entity.statements import get_http_url
+from spid_cie_oidc.entity.trust_chain_operations import get_or_create_trust_chain
+from spid_cie_oidc.relying_party.exceptions import ValidationException
+from spid_cie_oidc.relying_party.settings import (
+    RP_DEFAULT_PROVIDER_PROFILES,
+    RP_PROVIDER_PROFILES
+)
+
+logger = logging.getLogger(__name__)
+
+
+class SpidCieOidcRp:
+    """
+    Baseclass with common methods for RPs
+    """
+
+    def get_jwks_from_jwks_uri(self, jwks_uri: str) -> dict:
+        """
+        get jwks
+        """
+        try:
+            jwks_dict = get_http_url([jwks_uri], httpc_params=HTTPC_PARAMS).json()
+        except Exception as e:
+            logger.error(f"Failed to download jwks from {jwks_uri}: {e}")
+            return {}
+        return jwks_dict
+
+    def get_oidc_op(self, request) -> TrustChain:
+        """
+            get available trust to a specific OP
+        """
+        if not request.GET.get("provider", None):
+            logger.warning(
+                "Missing provider url. Please try '?provider=https://provider-subject/'"
+            )
+            raise InvalidTrustchain(
+                "Missing provider url. Please try '?provider=https://provider-subject/'"
+            )
+        trust_anchor = request.GET.get(
+            "trust_anchor",
+            settings.OIDCFED_IDENTITY_PROVIDERS.get(
+                request.GET["provider"],
+                settings.OIDCFED_DEFAULT_TRUST_ANCHOR
+            )
+        )
+
+        if trust_anchor not in settings.OIDCFED_TRUST_ANCHORS:
+            logger.warning("Unallowed Trust Anchor")
+            raise InvalidTrustchain("Unallowed Trust Anchor")
+
+        tc = TrustChain.objects.filter(
+            sub=request.GET["provider"],
+            trust_anchor__sub=trust_anchor,
+        ).first()
+
+        discover_trust = False
+        if not tc:
+            logger.info(f'Trust Chain not found for {request.GET["provider"]}')
+            discover_trust = True
+
+        elif not tc.is_active:
+            logger.warning(f"{tc} found but DISABLED at {tc.modified}")
+            raise InvalidTrustchain(f"{tc} found but DISABLED at {tc.modified}")
+
+        elif tc.is_expired:
+            logger.warning(f"{tc} found but expired at {tc.exp}")
+            logger.warning("Try to renew the trust chain")
+            discover_trust = True
+
+        if discover_trust:
+            tc = get_or_create_trust_chain(
+                subject=request.GET["provider"],
+                trust_anchor=trust_anchor,
+                # TODO - not sure that it's required for a RP that fetches OP directly from TA
+                # required_trust_marks = [],
+                force=True,
+            )
+        return tc
+
+    def validate_json_schema(self, request, schema_type, error_description):
+        try:
+            schema = RP_PROVIDER_PROFILES[RP_DEFAULT_PROVIDER_PROFILES]
+            schema[schema_type](**request)
+        except ValidationError as e:
+            logger.error(
+                f"{error_description} "
+                f"for {request.get('client_id', None)}: {e}"
+            )
+            raise ValidationException()
diff --git a/spid_cie_oidc/relying_party/views/rp_begin.py b/spid_cie_oidc/relying_party/views/rp_begin.py
new file mode 100644
index 00000000..e59cfa25
--- /dev/null
+++ b/spid_cie_oidc/relying_party/views/rp_begin.py
@@ -0,0 +1,177 @@
+import json
+import logging
+from copy import deepcopy
+
+from django.http import HttpResponseRedirect
+from django.shortcuts import render
+from django.utils import timezone
+from django.utils.module_loading import import_string
+from django.utils.translation import gettext as _
+from django.views import View
+from spid_cie_oidc.entity.exceptions import InvalidTrustchain
+from spid_cie_oidc.entity.jwtse import create_jws
+from spid_cie_oidc.entity.models import FederationEntityConfiguration
+from spid_cie_oidc.relying_party.settings import OIDCFED_ACR_PROFILES
+
+from ..models import OidcAuthentication
+from ..settings import (
+    RP_PKCE_CONF,
+    RP_REQUEST_CLAIM_BY_PROFILE
+)
+from ..utils import (
+    http_dict_to_redirect_uri_path,
+    random_string,
+)
+
+from . import SpidCieOidcRp
+
+logger = logging.getLogger(__name__)
+
+
+class SpidCieOidcRpBeginView(SpidCieOidcRp, View):
+    """
+        View which processes the actual Authz request and
+        returns a Http Redirect
+    """
+
+    error_template = "rp_error.html"
+
+    def get(self, request, *args, **kwargs):
+        """
+        http://localhost:8001/oidc/rp/authorization?
+        provider=http://127.0.0.1:8002/
+        """
+        try:
+            tc = self.get_oidc_op(request)
+            if not tc:
+                context = {
+                    "error": "request rejected",
+                    "error_description": "Trust Chain is unavailable.",
+                }
+                return render(request, self.error_template, context)
+
+        except InvalidTrustchain as exc:
+            context = {
+                "error": "request rejected",
+                "error_description": str(exc.args),
+            }
+            return render(request, self.error_template, context, status=404)
+
+        except Exception as exc:
+            context = {
+                "error": "request rejected",
+                "error_description": _(str(exc.args)),
+            }
+            return render(request, self.error_template, context)
+
+        provider_metadata = tc.metadata.get('openid_provider', None)
+        if not provider_metadata:
+            context = {
+                "error": "request rejected",
+                "error_description": _("provider metadata not found"),
+            }
+            return render(request, self.error_template, context, status=404)
+
+        entity_conf = FederationEntityConfiguration.objects.filter(
+            entity_type="openid_relying_party",
+            # TODO: RPs multitenancy?
+            # sub = request.build_absolute_uri()
+        ).first()
+
+        if not entity_conf:
+            context = {
+                "error": "request rejected",
+                "error_description": _("Missing configuration."),
+            }
+            return render(request, self.error_template, context, status=404)
+
+        client_conf = entity_conf.metadata["openid_relying_party"]
+
+        if not (
+            # TODO
+            # provider_metadata.get("jwks_uri", None)
+            # or
+            provider_metadata.get("jwks", None)
+        ):
+            context = {
+                "error": "request rejected",
+                "error_description": _("Invalid provider Metadata."),
+            }
+            return render(request, self.error_template, context, status=404)
+
+        if provider_metadata.get("jwks", None):
+            jwks_dict = provider_metadata["jwks"]
+        else:
+            jwks_dict = self.get_jwks_from_jwks_uri(provider_metadata["jwks_uri"])
+        if not jwks_dict:
+            _msg = f"Failed to get jwks from {tc.sub}"
+            logger.error(_msg)
+            context = {
+                "error": "request rejected",
+                "error_description": _msg
+            }
+            return render(request, self.error_template, context)
+
+        authz_endpoint = provider_metadata["authorization_endpoint"]
+
+        redirect_uri = request.GET.get("redirect_uri", client_conf["redirect_uris"][0])
+        if redirect_uri not in client_conf["redirect_uris"]:
+            logger.warning(
+                f"Requested for unknown redirect uri {redirect_uri}. "
+                f"Reverted to default {client_conf['redirect_uris'][0]}."
+            )
+            redirect_uri = client_conf["redirect_uris"][0]
+        _profile = request.GET.get("profile", "spid")
+        authz_data = dict(
+            scope= request.GET.get("scope", None) or "openid",
+            redirect_uri=redirect_uri,
+            response_type=client_conf["response_types"][0],
+            nonce=random_string(32),
+            state=random_string(32),
+            client_id=client_conf["client_id"],
+            endpoint=authz_endpoint,
+            acr_values= OIDCFED_ACR_PROFILES,
+            iat=int(timezone.localtime().timestamp()),
+            aud=[tc.sub, authz_endpoint],
+            claims=RP_REQUEST_CLAIM_BY_PROFILE[_profile],
+        )
+
+        _prompt = request.GET.get("prompt", "consent login")
+
+        # if "offline_access" in authz_data["scope"]:
+        # _prompt.extend(["consent login"])
+
+        authz_data["prompt"] = _prompt
+
+        # PKCE
+        pkce_func = import_string(RP_PKCE_CONF["function"])
+        pkce_values = pkce_func(**RP_PKCE_CONF["kwargs"])
+        authz_data.update(pkce_values)
+        #
+        authz_entry = dict(
+            client_id=client_conf["client_id"],
+            state=authz_data["state"],
+            endpoint=authz_endpoint,
+            # TODO: better have here an organization name
+            provider=tc.sub,
+            provider_id=tc.sub,
+            data=json.dumps(authz_data),
+            provider_jwks=json.dumps(jwks_dict),
+            provider_configuration=provider_metadata,
+        )
+
+        # TODO: Prune the old or unbounded authz ...
+        OidcAuthentication.objects.create(**authz_entry)
+
+        authz_data.pop("code_verifier")
+        # add the signed request object
+        authz_data_obj = deepcopy(authz_data)
+        authz_data_obj["iss"] = client_conf["client_id"]
+        authz_data_obj["sub"] = client_conf["client_id"]
+
+        request_obj = create_jws(authz_data_obj, entity_conf.jwks[0])
+        authz_data["request"] = request_obj
+        uri_path = http_dict_to_redirect_uri_path(authz_data)
+        url = "?".join((authz_endpoint, uri_path))
+        logger.info(f"Starting Authz request to {url}")
+        return HttpResponseRedirect(url)
diff --git a/spid_cie_oidc/relying_party/views/rp_callback.py b/spid_cie_oidc/relying_party/views/rp_callback.py
new file mode 100644
index 00000000..8e16b6d7
--- /dev/null
+++ b/spid_cie_oidc/relying_party/views/rp_callback.py
@@ -0,0 +1,262 @@
+import json
+import logging
+
+from django.conf import settings
+from django.contrib.auth import get_user_model, login
+from django.http import HttpResponseRedirect, JsonResponse
+from django.shortcuts import render
+from django.urls import reverse
+from django.utils.translation import gettext as _
+from django.views import View
+from spid_cie_oidc.entity.jwtse import (
+    unpad_jwt_head,
+    unpad_jwt_payload,
+    verify_jws
+)
+from spid_cie_oidc.entity.models import FederationEntityConfiguration
+from spid_cie_oidc.entity.settings import HTTPC_PARAMS
+from spid_cie_oidc.relying_party.exceptions import ValidationException
+
+from ..models import OidcAuthentication, OidcAuthenticationToken
+from ..oauth2 import *
+from ..oidc import *
+from ..settings import (
+    RP_ATTR_MAP,
+    RP_USER_CREATE,
+    RP_USER_LOOKUP_FIELD,
+)
+from ..utils import process_user_attributes
+
+from . import SpidCieOidcRp
+
+logger = logging.getLogger(__name__)
+
+
+class SpidCieOidcRpCallbackView(View, SpidCieOidcRp, OidcUserInfo, OAuth2AuthorizationCodeGrant):
+    """
+        View which processes an Authorization Response
+        https://tools.ietf.org/html/rfc6749#section-4.1.2
+
+        eg:
+        /redirect_uri?code=tYkP854StRqBVcW4Kg4sQfEN5Qz&state=R9EVqaazGsj3wg5JgxIgm8e8U4BMvf7W
+    """
+
+    error_template = "rp_error.html"
+
+    def user_reunification(self, user_attrs: dict):
+        user_model = get_user_model()
+        lookup = {
+            f"attributes__{RP_USER_LOOKUP_FIELD}": user_attrs[RP_USER_LOOKUP_FIELD]
+        }
+        user = user_model.objects.filter(**lookup).first()
+        if user:
+            user.attributes.update(user_attrs)
+            user.save()
+            logger.info(f"{RP_USER_LOOKUP_FIELD} matched on user {user}")
+            return user
+        elif RP_USER_CREATE:
+            user = user_model.objects.create(
+                username=user_attrs.get("username", user_attrs["sub"]),
+                first_name=user_attrs.get("given_name", user_attrs["sub"]),
+                last_name=user_attrs.get("family_name", user_attrs["sub"]),
+                email=user_attrs.get("email", ""),
+                attributes=user_attrs,
+            )
+            logger.info(f"Created new user {user}")
+            return user
+
+    def get_jwk_from_jwt(self, jwt: str, provider_jwks: dict) -> dict:
+        """
+            docs here
+        """
+        head = unpad_jwt_head(jwt)
+        kid = head["kid"]
+        for jwk in provider_jwks:
+            if jwk["kid"] == kid:
+                return jwk
+        return {}
+
+    def get(self, request, *args, **kwargs):
+        """
+            The Authorization callback, the redirect uri where the auth code lands
+        """
+        request_args = {k: v for k, v in request.GET.items()}
+        if "error" in request_args:
+            return render(
+                request,
+                self.error_template,
+                request_args,
+                status=401
+            )
+        authz = OidcAuthentication.objects.filter(
+            state=request_args.get("state"),
+        )
+        try:
+            self.validate_json_schema(
+                request.GET.dict(),
+                "authn_response",
+                "Authn response object validation failed"
+            )
+        except ValidationException:
+            return JsonResponse(
+                {
+                    "error": "invalid_request",
+                    "error_description": "Authn response object validation failed",
+                },
+                status = 400
+            )
+
+        if not authz:
+            # TODO: verify error message and status
+            context = {
+                "error": "unauthorized request",
+                "error_description": _("Authentication not found"),
+            }
+            return render(request, self.error_template, context, status=401)
+        else:
+            authz = authz.last()
+
+        code = request.GET.get("code")
+        if not code:
+            # TODO: verify error message and status
+            context = {
+                "error": "invalid request",
+                "error_description": _("Request MUST contain code"),
+            }
+            return render(request, self.error_template, context, status=400)
+
+        authz_token = OidcAuthenticationToken.objects.create(
+            authz_request=authz, code=code
+        )
+        self.rp_conf = FederationEntityConfiguration.objects.filter(
+            sub=authz_token.authz_request.client_id
+        ).first()
+        if not self.rp_conf:
+            # TODO: verify error message and status
+            context = {
+                "error": "invalid request",
+                "error_description": _("Relay party not found"),
+            }
+            return render(request, self.error_template, context, status=400)
+        authz_data = json.loads(authz.data)
+        token_response = self.access_token_request(
+            redirect_uri=authz_data["redirect_uri"],
+            state=authz.state,
+            code=code,
+            issuer_id=authz.provider_id,
+            client_conf=self.rp_conf,
+            token_endpoint_url=authz.provider_configuration["token_endpoint"],
+            audience=[authz.provider_id],
+            code_verifier=authz_data.get("code_verifier"),
+        )
+        if not token_response:
+            # TODO: verify error message
+            context = {
+                "error": "invalid token response",
+                "error_description": _("Token response seems not to be valid"),
+            }
+            return render(request, self.error_template, context, status=400)
+
+        else:
+            try:
+                self.validate_json_schema(
+                    token_response,
+                    "token_response",
+                    "Token response object validation failed"
+                )
+            except ValidationException:
+                return JsonResponse(
+                    {
+                        "error": "invalid_request",
+                        "error_description": "Token response object validation failed",
+                    },
+                    status = 400
+                )
+        jwks = authz.provider_configuration["jwks"]["keys"]
+        access_token = token_response["access_token"]
+        id_token = token_response["id_token"]
+        op_ac_jwk = self.get_jwk_from_jwt(access_token, jwks)
+        op_id_jwk = self.get_jwk_from_jwt(id_token, jwks)
+
+        if not op_ac_jwk or not op_id_jwk:
+            # TODO: verify error message and status
+            context = {
+                "error": "invalid token",
+                "error_description": _("Authentication token seems not to be valid."),
+            }
+            return render(request, self.error_template, context, status=403)
+
+        try:
+            verify_jws(access_token, op_ac_jwk)
+        except Exception:
+            # TODO: verify error message
+            context = {
+                "error": "token verification failed",
+                "error_description": _("Authentication token validation error."),
+            }
+            return render(request, self.error_template, context, status=403)
+
+        try:
+            verify_jws(id_token, op_id_jwk)
+        except Exception:
+            # TODO: verify error message
+            context = {
+                "error": "token verification failed",
+                "error_description": _("ID token validation error."),
+            }
+            return render(request, self.error_template, context, status=403)
+
+        decoded_id_token = unpad_jwt_payload(id_token)
+        logger.debug(decoded_id_token)
+
+        decoded_access_token = unpad_jwt_payload(access_token)
+        logger.debug(decoded_access_token)
+
+        authz_token.access_token = access_token
+        authz_token.id_token = id_token
+        authz_token.scope = token_response.get("scope")
+        authz_token.token_type = token_response["token_type"]
+        authz_token.expires_in = token_response["expires_in"]
+        authz_token.save()
+        userinfo = self.get_userinfo(
+            authz.state,
+            authz_token.access_token,
+            authz.provider_configuration,
+            verify=HTTPC_PARAMS,
+        )
+        if not userinfo:
+            # TODO: verify error message
+            context = {
+                "error": "invalid userinfo response",
+                "error_description": _("UserInfo response seems not to be valid"),
+            }
+            return render(request, self.error_template, context, status=400)
+
+        # here django user attr mapping
+        user_attrs = process_user_attributes(userinfo, RP_ATTR_MAP, authz.__dict__)
+        if not user_attrs:
+            _msg = "No user attributes have been processed"
+            logger.warning(f"{_msg}: {userinfo}")
+            # TODO: verify error message and status
+            context = {
+                "error": "missing user attributes",
+                "error_description": _(f"{_msg}: {userinfo}"),
+            }
+            return render(request, self.error_template, context, status=403)
+
+        user = self.user_reunification(user_attrs)
+        if not user:
+            # TODO: verify error message and status
+            context = {"error": _("No user found"), "error_description": _("")}
+            return render(request, self.error_template, context, status=403)
+
+        # authenticate the user
+        login(request, user)
+        request.session["oidc_rp_user_attrs"] = user_attrs
+        authz_token.user = user
+        authz_token.save()
+        return HttpResponseRedirect(
+            getattr(
+                settings, "LOGIN_REDIRECT_URL", None
+            ) or reverse("spid_cie_rp_echo_attributes")
+        )
diff --git a/spid_cie_oidc/relying_party/views/rp_callback_echo_attributes.py b/spid_cie_oidc/relying_party/views/rp_callback_echo_attributes.py
new file mode 100644
index 00000000..27d78e7b
--- /dev/null
+++ b/spid_cie_oidc/relying_party/views/rp_callback_echo_attributes.py
@@ -0,0 +1,15 @@
+import logging
+
+from django.shortcuts import render
+from django.views import View
+
+
+logger = logging.getLogger(__name__)
+
+
+class SpidCieOidcRpCallbackEchoAttributes(View):
+    template = "echo_attributes.html"
+
+    def get(self, request):
+        data = {"oidc_rp_user_attrs": request.session.get("oidc_rp_user_attrs", {})}
+        return render(request, self.template, data)
diff --git a/spid_cie_oidc/relying_party/views/rp_initiated_logout.py b/spid_cie_oidc/relying_party/views/rp_initiated_logout.py
new file mode 100644
index 00000000..7408f028
--- /dev/null
+++ b/spid_cie_oidc/relying_party/views/rp_initiated_logout.py
@@ -0,0 +1,83 @@
+import logging
+
+import requests
+from django.conf import settings
+from django.contrib.auth import logout
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponseRedirect
+from django.urls import reverse
+from django.utils import timezone
+from spid_cie_oidc.entity.jwtse import create_jws
+from spid_cie_oidc.entity.models import FederationEntityConfiguration
+from ..models import OidcAuthenticationToken
+from ..oauth2 import *
+from ..oidc import *
+
+logger = logging.getLogger(__name__)
+
+
+@login_required
+def oidc_rpinitiated_logout(request):
+    """
+        Call the token revocation endpoint of the op
+    """
+    auth_tokens = OidcAuthenticationToken.objects.filter(
+        user=request.user
+    ).filter(revoked__isnull=True)
+
+    default_logout_url = getattr(
+        settings, "LOGOUT_REDIRECT_URL", None
+    ) or reverse("spid_cie_rp_landing")
+    if not auth_tokens:
+        logger.warning(
+            "Token revocation failed: not found any authentication session"
+        )
+        return HttpResponseRedirect(default_logout_url)
+
+    auth_token = auth_tokens.last()
+    authz = auth_token.authz_request
+    provider_conf = authz.provider_configuration
+    revocation_endpoint_url = provider_conf.get("revocation_endpoint")
+
+    # first of all on RP side ...
+    logger.info(f"{request.user} logout")
+    logout(request)
+    if not revocation_endpoint_url:
+        logger.warning(
+            f"{authz.provider_id} doesn't expose the token revocation endpoint."
+        )
+        return HttpResponseRedirect(default_logout_url)
+    else:
+        rp_conf = FederationEntityConfiguration.objects.filter(
+            sub= authz.client_id,
+            is_active=True,
+        ).first()
+
+        # private_key_jwt
+        client_assertion = create_jws(
+            {
+                "iss": authz.client_id,
+                "sub": authz.client_id,
+                "aud": [revocation_endpoint_url],
+                "iat": iat_now(),
+                "exp": exp_from_now(),
+                "jti": str(uuid.uuid4()),
+            },
+            jwk_dict=rp_conf.jwks[0],
+        )
+
+        auth_token.logged_out = timezone.localtime()
+        auth_token.save()
+
+        revocation_request = dict(
+            token = auth_token.access_token,
+            client_id = authz.client_id,
+            client_assertion = client_assertion,
+            client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
+        )
+        try:
+            requests.post(revocation_endpoint_url, data = revocation_request)
+        except Exception as e:
+            logger.warning(f"Token revocation failed: {e}")
+        auth_tokens.update(revoked = timezone.localtime())
+        return HttpResponseRedirect(default_logout_url)
diff --git a/spid_cie_oidc/relying_party/views/rp_landing.py b/spid_cie_oidc/relying_party/views/rp_landing.py
new file mode 100644
index 00000000..5faa91ef
--- /dev/null
+++ b/spid_cie_oidc/relying_party/views/rp_landing.py
@@ -0,0 +1,29 @@
+import logging
+import random
+
+from django.conf import settings
+from django.shortcuts import render
+from spid_cie_oidc.entity.models import TrustChain
+
+logger = logging.getLogger(__name__)
+
+
+def oidc_rp_landing(request):
+    trust_chains = TrustChain.objects.filter(
+        metadata__openid_provider__isnull=False,
+        is_active=True
+    )
+    spid_providers = []
+    cie_providers = []
+    for tc in trust_chains:
+        if tc.is_active:
+            if tc.sub in settings.OIDCFED_IDENTITY_PROVIDERS.get("spid", []):
+                spid_providers.append(tc)
+            elif tc.sub in settings.OIDCFED_IDENTITY_PROVIDERS.get("cie", []):
+                cie_providers.append(tc)
+    random.shuffle(spid_providers)
+    content = {
+        "spid_providers": spid_providers,
+        "cie_providers" : cie_providers
+    }
+    return render(request, "rp_landing.html", content)


Test patch:
diff --git a/spid_cie_oidc/authority/tests/test_02_trust_anchor_intermediary.py b/spid_cie_oidc/authority/tests/test_02_trust_anchor_intermediary.py
index 218e5919..4e5173ac 100644
--- a/spid_cie_oidc/authority/tests/test_02_trust_anchor_intermediary.py
+++ b/spid_cie_oidc/authority/tests/test_02_trust_anchor_intermediary.py
@@ -73,8 +73,7 @@ def test_trust_chain_valid_no_intermediaries(self, mocked):
 
         trust_chain = trust_chain_builder(
             subject=rp_onboarding_data["sub"],
-            trust_anchor=trust_anchor_ec,
-            metadata_type="openid_relying_party",
+            trust_anchor=trust_anchor_ec
         )
         self.assertTrue(trust_chain)
         self.assertTrue(trust_chain.final_metadata)
@@ -119,8 +118,7 @@ def test_trust_chain_valid_with_intermediary(self, mocked):
 
         trust_chain = trust_chain_builder(
             subject=self.rp.sub,
-            trust_anchor=trust_anchor_ec,
-            metadata_type="openid_relying_party",
+            trust_anchor=trust_anchor_ec
         )
 
         self.assertTrue(trust_chain)
@@ -144,7 +142,6 @@ def test_trust_chain_valid_with_intermediary(self, mocked):
 
         stored_trust_chain = TrustChain.objects.create(
             sub=trust_chain.subject,
-            type=trust_chain.metadata_type,
             exp=trust_chain.exp_datetime,
             chain=trust_chain.serialize(),
             metadata=trust_chain.final_metadata,
@@ -170,8 +167,7 @@ def test_trust_chain_valid_with_intermediaries_many_authhints(self, mocked):
 
         trust_chain = trust_chain_builder(
             subject=self.rp.sub,
-            trust_anchor=trust_anchor_ec,
-            metadata_type="openid_relying_party",
+            trust_anchor=trust_anchor_ec
         )
 
         for ec in trust_chain.trust_path:
@@ -197,7 +193,6 @@ def test_trust_chain_valid_with_intermediaries_and_trust_mark_filter(self, mocke
         trust_chain = trust_chain_builder(
             subject=self.rp.sub,
             trust_anchor=trust_anchor_ec,
-            metadata_type="openid_relying_party",
             required_trust_marks=["https://www.spid.gov.it/certification/rp"],
         )
 
@@ -220,7 +215,6 @@ def test_trust_chain_with_missing_trust_mark(self, mocked):
             trust_chain = trust_chain_builder(
                 subject=self.rp.sub,
                 trust_anchor=trust_anchor_ec,
-                metadata_type="openid_relying_party",
                 required_trust_marks=["https://www.spid.gov.it/certification/rp"],
             )
 
@@ -232,8 +226,7 @@ def test_trust_chain_valid_helpers(self, mocked):
             subject=rp_conf["sub"],
             trust_anchor=self.ta_conf.sub,
             # TODO
-            # required_trust_marks: list = [],
-            metadata_type="openid_relying_party",
+            # required_trust_marks: list = []
         )
 
         self.assertFalse(gctc.is_expired)
@@ -246,14 +239,17 @@ def test_resolve_endpoint(self, mocked):
             subject=rp_conf["sub"],
             trust_anchor=self.ta_conf.sub,
             # TODO
-            # required_trust_marks: list = [],
-            metadata_type="openid_relying_party",
+            # required_trust_marks: list = []
         )
 
         url = reverse("oidcfed_resolve")
 
         c = Client()
-        res = c.get(url, data={"sub": self.rp.sub, "anchor": self.ta_conf.sub})
+        res = c.get(url, data={
+                "sub": self.rp.sub, 
+                "anchor": self.ta_conf.sub
+            }
+        )
         self.assertTrue(res.status_code == 200)
         verify_jws(res.content.decode(), self.ta_conf.jwks[0])
 
diff --git a/spid_cie_oidc/entity/tests/op_metadata_settings.py b/spid_cie_oidc/entity/tests/op_metadata_settings.py
index 75116376..1aeb53d8 100644
--- a/spid_cie_oidc/entity/tests/op_metadata_settings.py
+++ b/spid_cie_oidc/entity/tests/op_metadata_settings.py
@@ -39,7 +39,6 @@
 OP_METADATA_CIE["response_types_supported"] = ["code"]
 OP_METADATA_CIE["response_modes_supported"] = ["query", "form_post"]
 OP_METADATA_CIE["grant_types_supported"] = ["authorization_code", "refresh_token"]
-OP_METADATA_CIE["acr_values_supported"] = ["CIE_L1", "CIE_L3"]
 OP_METADATA_CIE["claims_supported"] = [
     "given_name",
     "family_name",
diff --git a/spid_cie_oidc/onboarding/tests/authn_request_settings.py b/spid_cie_oidc/onboarding/tests/authn_request_settings.py
index c1c76e18..e193fc50 100644
--- a/spid_cie_oidc/onboarding/tests/authn_request_settings.py
+++ b/spid_cie_oidc/onboarding/tests/authn_request_settings.py
@@ -117,7 +117,6 @@
 AUTHN_REQUEST_CIE = deepcopy(AUTHN_REQUEST_SPID)
 AUTHN_REQUEST_CIE["scope"] = ["openid", "offline_access", "email", "profile"]
 AUTHN_REQUEST_CIE["prompt"] = "consent login"
-AUTHN_REQUEST_CIE["acr_values"] = ["CIE_L2", "CIE_L1"]
 AUTHN_REQUEST_CIE["claims"] = CLAIMS_CIE
 
 AUTHN_REQUEST_CIE_NO_ACR_VALUES = deepcopy(AUTHN_REQUEST_CIE)
diff --git a/spid_cie_oidc/onboarding/tests/test_02_tools.py b/spid_cie_oidc/onboarding/tests/test_02_tools.py
index 2f083d0d..dd042b84 100644
--- a/spid_cie_oidc/onboarding/tests/test_02_tools.py
+++ b/spid_cie_oidc/onboarding/tests/test_02_tools.py
@@ -96,7 +96,6 @@ def test_decode_and_verify_jwt(self):
         self.assertEqual(res.status_code, 200)
         res = self.client.post(url, {"jwt":jwt})
         self.assertEqual(res.status_code, 200)
-        self.assertIsNotNone(res.context['jwt'])
         self.assertIsNotNone(res.context['payload'])
         self.assertIsNotNone(res.context['head'])
         jwkError = {"jwkerror":"error"}
diff --git a/spid_cie_oidc/provider/tests/test_02_authn_endpoint.py b/spid_cie_oidc/provider/tests/test_02_authn_endpoint.py
index 70f09f59..864a9862 100644
--- a/spid_cie_oidc/provider/tests/test_02_authn_endpoint.py
+++ b/spid_cie_oidc/provider/tests/test_02_authn_endpoint.py
@@ -69,9 +69,8 @@ def setUp(self):
         )
         self.trust_chain = TrustChain.objects.create(
             sub=RP_SUB,
-            type="openid_relying_party",
             exp=datetime_from_timestamp(exp_from_now(33)),
-            metadata=RP_METADATA["openid_relying_party"],
+            metadata=RP_METADATA,
             status="valid",
             trust_anchor=self.ta_fes,
             is_active=True,
@@ -271,7 +270,7 @@ def test_auth_request_trust_chain_no_active(self):
     @override_settings(OIDCFED_DEFAULT_TRUST_ANCHOR=TA_SUB)
     def test_auth_request_invalid_jwk(self):
         jws = create_jws(self.REQUEST_OBJECT_PAYLOAD, RP_METADATA_JWK1)
-        self.trust_chain.metadata["jwks"]["keys"][0][
+        self.trust_chain.metadata['openid_relying_party']["jwks"]["keys"][0][
             "kid"
         ] = "31ALfVXx9dcAMMHCVvh42qLTlanBL_r6BTnD5uMDzFT"
         self.trust_chain.save()
@@ -281,7 +280,7 @@ def test_auth_request_invalid_jwk(self):
         self.assertTrue(res.status_code == 302)
         self.assertIn("error=invalid_request", res.url)
         self.assertIn("state", res.url)
-        self.trust_chain.metadata["jwks"]["keys"][0][
+        self.trust_chain.metadata['openid_relying_party']["jwks"]["keys"][0][
             "kid"
         ] = RP_METADATA_JWK1['kid']
         self.trust_chain.save()
diff --git a/spid_cie_oidc/provider/tests/test_03_refresh_token.py b/spid_cie_oidc/provider/tests/test_03_refresh_token.py
index 5affe994..c125c924 100644
--- a/spid_cie_oidc/provider/tests/test_03_refresh_token.py
+++ b/spid_cie_oidc/provider/tests/test_03_refresh_token.py
@@ -42,9 +42,8 @@ def setUp(self):
         )
         self.trust_chain = TrustChain.objects.create(
             sub=RP_SUB,
-            type="openid_relying_party",
             exp=datetime_from_timestamp(exp_from_now(33)),
-            metadata=RP_METADATA["openid_relying_party"],
+            metadata=RP_METADATA,
             status="valid",
             trust_anchor=self.ta_fes,
             is_active=True,
@@ -70,7 +69,7 @@ def setUp(self):
             user=User.objects.create(username = "username"),
             user_uid="",
             nonce="",
-            authz_request={"scope": "openid", "nonce": "123", "acr_values":["https://www.spid.gov.it/SpidL2"]},
+            authz_request={"scope": "offline_access", "prompt": "consent", "nonce": "123", "acr_values":["https://www.spid.gov.it/SpidL2"]},
             client_id="",
             auth_code="code",
         )
@@ -91,6 +90,7 @@ def test_grant_refresh_token(self):
             grant_type="refresh_token",
             code = "code",
             code_verifier = "code_verifier"
+
         )
         res = client.post(url, request)
         self.assertTrue(res.status_code == 200)
diff --git a/spid_cie_oidc/provider/tests/test_04_userinfo_endpoint.py b/spid_cie_oidc/provider/tests/test_04_userinfo_endpoint.py
index 7c12c1e2..108ab0f2 100644
--- a/spid_cie_oidc/provider/tests/test_04_userinfo_endpoint.py
+++ b/spid_cie_oidc/provider/tests/test_04_userinfo_endpoint.py
@@ -92,9 +92,8 @@ def test_userinfo_endpoint_ok(self):
         headers = self.define_db()
         self.trust_chain = TrustChain.objects.create(
             sub=self.RP_SUB,
-            type="openid_relying_party",
             exp=datetime_from_timestamp(exp_from_now(33)),
-            metadata=RP_METADATA["openid_relying_party"],
+            metadata=RP_METADATA,
             status="valid",
             trust_anchor=self.ta_fes,
             is_active=True,
diff --git a/spid_cie_oidc/provider/tests/test_05_introspection_endpoint.py b/spid_cie_oidc/provider/tests/test_05_introspection_endpoint.py
index 586ba91b..59d773b9 100644
--- a/spid_cie_oidc/provider/tests/test_05_introspection_endpoint.py
+++ b/spid_cie_oidc/provider/tests/test_05_introspection_endpoint.py
@@ -74,9 +74,8 @@ def setUp(self):
         )
         self.trust_chain = TrustChain.objects.create(
             sub=self.RP_SUB,
-            type="openid_relying_party",
             exp=datetime_from_timestamp(exp_from_now(33)),
-            metadata=RP_METADATA["openid_relying_party"],
+            metadata=RP_METADATA,
             status="valid",
             trust_anchor=self.ta_fes,
             is_active=True,
@@ -95,3 +94,16 @@ def test_introspection_endpoint(self):
         res = client.post(url, request)
         self.assertTrue(res.status_code == 200)
         self.assertTrue("openid" in res.content.decode())
+
+    def test_introspection_endpoint_validation_error(self):
+        client = Client()
+        url = reverse("introspection_endpoint")
+        request = {
+            "client_assertion" : self.ca_jws,
+            "client_assertion_type" : "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
+            "token" : self.jwt_token
+
+        }
+        res = client.post(url, request)
+        self.assertTrue(res.status_code == 400)
+        self.assertTrue(res.json()["error"] == "invalid_request")
diff --git a/spid_cie_oidc/provider/tests/test_07_fetch_relying_parties.py b/spid_cie_oidc/provider/tests/test_07_fetch_relying_parties.py
index 8827d4c5..4df6b506 100644
--- a/spid_cie_oidc/provider/tests/test_07_fetch_relying_parties.py
+++ b/spid_cie_oidc/provider/tests/test_07_fetch_relying_parties.py
@@ -41,9 +41,8 @@ def create_tc():
     )
     return TrustChain.objects.create(
         sub=RP_CONF_AS_JSON["sub"],
-        type="openid_relying_party",
         exp=EXP,
-        metadata=RP_CONF_AS_JSON["metadata"]["openid_relying_party"],
+        metadata=RP_CONF_AS_JSON["metadata"],
         status="valid",
         trust_anchor=ta_fes,
         is_active=True,
diff --git a/spid_cie_oidc/provider/tests/test_08_token_endpoint.py b/spid_cie_oidc/provider/tests/test_08_token_endpoint.py
index 7e47d2ed..df91a6c4 100644
--- a/spid_cie_oidc/provider/tests/test_08_token_endpoint.py
+++ b/spid_cie_oidc/provider/tests/test_08_token_endpoint.py
@@ -42,9 +42,8 @@ def setUp(self):
         )
         self.trust_chain = TrustChain.objects.create(
             sub=RP_SUB,
-            type="openid_relying_party",
             exp=datetime_from_timestamp(exp_from_now(33)),
-            metadata=RP_METADATA["openid_relying_party"],
+            metadata=RP_METADATA,
             status="valid",
             trust_anchor=self.ta_fes,
             is_active=True,
diff --git a/spid_cie_oidc/provider/tests/test_09_revocation_endpoint.py b/spid_cie_oidc/provider/tests/test_09_revocation_endpoint.py
index 0d57ddae..228c9a41 100644
--- a/spid_cie_oidc/provider/tests/test_09_revocation_endpoint.py
+++ b/spid_cie_oidc/provider/tests/test_09_revocation_endpoint.py
@@ -75,9 +75,8 @@ def setUp(self):
 
         TrustChain.objects.create(
             sub=RP_CONF_AS_JSON["sub"],
-            type="openid_relying_party",
             exp=datetime_from_timestamp(exp_from_now(33)),
-            metadata=RP_CONF_AS_JSON["metadata"]["openid_relying_party"],
+            metadata=RP_CONF_AS_JSON["metadata"],
             status="valid",
             trust_anchor=ta_fes,
             is_active=True,
diff --git a/spid_cie_oidc/provider/tests/test_10_no_consent.py b/spid_cie_oidc/provider/tests/test_10_no_consent.py
new file mode 100644
index 00000000..82f31132
--- /dev/null
+++ b/spid_cie_oidc/provider/tests/test_10_no_consent.py
@@ -0,0 +1,18 @@
+
+from django.test import Client, TestCase
+from django.urls import reverse
+from spid_cie_oidc.authority.tests.settings import rp_conf
+
+
+class NoConsentTest(TestCase):
+
+    def setUp(self):
+        pass
+
+    def test_no_consent(self):
+        client = Client()
+        url = reverse("oidc_provider_not_consent")
+        redirect_uri = rp_conf["metadata"]["openid_relying_party"]["redirect_uris"][0]
+        res = client.get(url + "?redirect_uri=" + redirect_uri)
+        self.assertTrue(res.status_code == 302)
+        self.assertTrue("Authentication+request+rejected+by+user" in res.url)
diff --git a/spid_cie_oidc/provider/tests/test_11_user_access_history.py b/spid_cie_oidc/provider/tests/test_11_user_access_history.py
new file mode 100644
index 00000000..1bb79171
--- /dev/null
+++ b/spid_cie_oidc/provider/tests/test_11_user_access_history.py
@@ -0,0 +1,72 @@
+
+from django.contrib.auth import get_user_model
+from django.test import Client, TestCase
+from django.urls import reverse
+from spid_cie_oidc.authority.tests.settings import rp_conf
+from spid_cie_oidc.provider.models import OidcSession
+
+
+class UserAccessHistoryTest(TestCase):
+
+    def setUp(self): 
+        self.user = get_user_model().objects.create(
+            username="test",
+            first_name="test",
+            last_name="test",
+            email="test@test.it",
+            is_staff = True
+        )
+        self.user.set_password("test")
+        self.user.save()
+        OidcSession.objects.create(
+            user=self.user,
+            user_uid="uid",
+            nonce="",
+            authz_request={
+                "scope": "openid", 
+                "nonce": "123", 
+                "acr_values" : ["https://www.spid.gov.it/SpidL2"],            
+                "redirect_uri" : rp_conf["metadata"]["openid_relying_party"]["redirect_uris"][0],
+                "state" : "state",
+            },
+            client_id="",
+            auth_code="auth_code",
+        )
+        OidcSession.objects.create(
+            user=self.user,
+            user_uid="uid",
+            nonce="1234",
+            authz_request={
+                "scope": "openid", 
+                "nonce": "1234", 
+                "acr_values" : ["https://www.spid.gov.it/SpidL2"],            
+                "redirect_uri" : rp_conf["metadata"]["openid_relying_party"]["redirect_uris"][0],
+                "state" : "state",
+            },
+            client_id="",
+            auth_code="code",
+        )
+
+
+    def test_user_access_history(self):
+        client = Client()
+        client.login(username="test", password="test")
+        session = client.session
+        session.update({"oidc": {"auth_code": "code"}, "user_uid" : "uid"})
+        session.save()
+        url = reverse("oidc_provider_access_history")
+        res = client.get(url)
+        self.assertTrue(res.status_code == 200)
+        self.assertTrue("auth_code=auth_code" in res.content.decode())
+
+    def test_user_access_history_revoke(self):
+        client = Client()
+        client.login(username="test", password="test")
+        session = client.session
+        session.update({"oidc": {"auth_code": "code"}, "user_uid" : "uid"})
+        session.save()
+        url = reverse("oidc_provider_revoke_session")
+        res = client.get(url, {"auth_code": "auth_code"})
+        self.assertTrue(res.status_code == 302)
+        url_history = reverse("oidc_provider_access_history")
+        self.assertTrue(url_history in res.url)
diff --git a/spid_cie_oidc/relying_party/tests/test_03_rp_begin.py b/spid_cie_oidc/relying_party/tests/test_03_rp_begin.py
index d46e2f4a..12c29d5a 100644
--- a/spid_cie_oidc/relying_party/tests/test_03_rp_begin.py
+++ b/spid_cie_oidc/relying_party/tests/test_03_rp_begin.py
@@ -29,9 +29,8 @@ def setUp(self):
         )
         self.trust_chain = TrustChain.objects.create(
             sub=op_conf["sub"],
-            type="openid_relying_party",
             exp=EXP,
-            metadata=op_conf["metadata"]["openid_provider"],
+            metadata=op_conf["metadata"],
             status="valid",
             trust_anchor=self.ta_fes,
             is_active=True,
@@ -45,7 +44,7 @@ def test_rp_begin(self):
         self.assertTrue(res.status_code == 302)
 
     @override_settings(OIDCFED_DEFAULT_TRUST_ANCHOR=TA_SUB, OIDCFED_TRUST_ANCHORS=[TA_SUB])
-    @patch("spid_cie_oidc.relying_party.views.SpidCieOidcRpBeginView.get_oidc_op", return_value=None)
+    @patch("spid_cie_oidc.relying_party.views.rp_begin.SpidCieOidcRpBeginView.get_oidc_op", return_value=None)
     def test_rp_begin_no_tc(self, mocked):
         client = Client()
         url = reverse("spid_cie_rp_begin")
@@ -56,7 +55,7 @@ def test_no_request(self):
         client = Client()
         url = reverse("spid_cie_rp_begin")
         res = client.get(url, {"request": {}})
-        self.assertTrue(res.status_code == 200)
+        self.assertTrue(res.status_code == 404)
         self.assertTrue("request rejected" in res.content.decode())
         self.assertTrue("Missing provider url" in res.content.decode())
 
@@ -66,7 +65,7 @@ def test_no_unallowed_tc(self):
         client = Client()
         url = reverse("spid_cie_rp_begin")
         res = client.get(url, {"provider": "provider"})
-        self.assertTrue(res.status_code == 200)
+        self.assertTrue(res.status_code == 404)
         self.assertTrue("request rejected" in res.content.decode())
         self.assertTrue("Unallowed Trust Anchor" in res.content.decode())
 
@@ -76,19 +75,20 @@ def test_no_rp_entity_conf(self):
         client = Client()
         url = reverse("spid_cie_rp_begin")
         res = client.get(url, {"provider": op_conf["sub"], "trust_anchor": TA_SUB})
-        self.assertTrue(res.status_code == 200)
+        self.assertTrue(res.status_code == 404)
         self.assertTrue("request rejected" in res.content.decode())
         self.assertTrue("Missing configuration" in res.content.decode())
 
     @override_settings(OIDCFED_DEFAULT_TRUST_ANCHOR=TA_SUB, OIDCFED_TRUST_ANCHORS=[TA_SUB])
     def test_invalid_provider_metadata(self):
-        metadata = deepcopy(op_conf["metadata"]["openid_provider"])
-        self.trust_chain.metadata = metadata.pop("jwks")
+        metadata = deepcopy(op_conf["metadata"])
+        metadata["openid_provider"].pop("jwks")
+        self.trust_chain.metadata = metadata
         self.trust_chain.save()
         client = Client()
         url = reverse("spid_cie_rp_begin")
         res = client.get(url, {"provider": op_conf["sub"], "trust_anchor": TA_SUB})
-        self.assertTrue(res.status_code == 200)
+        self.assertTrue(res.status_code == 404)
         self.assertTrue("request rejected" in res.content.decode())
         self.assertTrue("Invalid provider Metadata" in res.content.decode())
 
@@ -99,20 +99,20 @@ def test_tc_no_active(self):
         client = Client()
         url = reverse("spid_cie_rp_begin")
         res = client.get(url, {"provider": op_conf["sub"], "trust_anchor": TA_SUB})
-        self.assertTrue(res.status_code == 200)
+        self.assertTrue(res.status_code == 404)
         self.assertTrue("request rejected" in res.content.decode())
         self.assertTrue("found but DISABLED at" in res.content.decode())
 
     @override_settings(OIDCFED_DEFAULT_TRUST_ANCHOR=TA_SUB, OIDCFED_TRUST_ANCHORS=[TA_SUB])
     def test_failed_to_get_jwks(self):
-        metadata = deepcopy(op_conf["metadata"]["openid_provider"])
-        metadata.pop("jwks")
+        metadata = deepcopy(op_conf["metadata"])
+        metadata['openid_provider'].pop("jwks")
         metadata["jwks_uri"] = "jwks_uri"
         self.trust_chain.metadata = metadata
         self.trust_chain.save()
         client = Client()
         url = reverse("spid_cie_rp_begin")
         res = client.get(url, {"provider": op_conf["sub"], "trust_anchor": TA_SUB})
-        self.assertTrue(res.status_code == 200)
+        
+        self.assertTrue(res.status_code == 404)
         self.assertTrue("request rejected" in res.content.decode())
-        self.assertTrue("Failed to get jwks from " in res.content.decode())
diff --git a/spid_cie_oidc/relying_party/tests/test_04_rp_callback.py b/spid_cie_oidc/relying_party/tests/test_04_rp_callback.py
index 031ba9e1..ffb7e46a 100644
--- a/spid_cie_oidc/relying_party/tests/test_04_rp_callback.py
+++ b/spid_cie_oidc/relying_party/tests/test_04_rp_callback.py
@@ -59,7 +59,7 @@ def test_rp_callback(self, mocked, mocked_2):
         )
 
     @override_settings(HTTP_CLIENT_SYNC=True)
-    @patch("spid_cie_oidc.relying_party.views.process_user_attributes", return_value=None)
+    @patch("spid_cie_oidc.relying_party.views.rp_callback.process_user_attributes", return_value=None)
     @patch("requests.post", return_value=MockedTokenEndPointResponse())
     @patch("requests.get", return_value=MockedUserInfoResponse())
     def test_rp_callback_no_rp_attr_map(self, mocked, mocked_2, mocked_3):
@@ -69,7 +69,7 @@ def test_rp_callback_no_rp_attr_map(self, mocked, mocked_2, mocked_3):
         self.assertTrue(res.status_code == 403)
 
     @override_settings(HTTP_CLIENT_SYNC=True)
-    @patch("spid_cie_oidc.relying_party.views.process_user_attributes", return_value=None)
+    @patch("spid_cie_oidc.relying_party.views.rp_callback.process_user_attributes", return_value=None)
     @patch("requests.post", return_value=MockedTokenEndPointResponse())
     @patch("requests.get", return_value=MockedUserInfoResponse())
     def test_rp_callback_incorret_request(self, mocked, mocked_2, mocked_3):
@@ -79,9 +79,9 @@ def test_rp_callback_incorret_request(self, mocked, mocked_2, mocked_3):
         self.assertTrue("error" in res.json())
 
     @override_settings(HTTP_CLIENT_SYNC=True)
-    @patch("spid_cie_oidc.relying_party.views.process_user_attributes", return_value=None)
+    @patch("spid_cie_oidc.relying_party.views.rp_callback.process_user_attributes", return_value=None)
     @patch("requests.post", return_value=MockedTokenEndPointResponse())
-    @patch("spid_cie_oidc.relying_party.views.SpidCieOidcRpCallbackView.get_userinfo", return_value=None)
+    @patch("spid_cie_oidc.relying_party.views.rp_callback.SpidCieOidcRpCallbackView.get_userinfo", return_value=None)
     def test_rp_callback_no_userinfo(self, mocked, mocked_2, mocked_3):
         client = Client()
         url = reverse("spid_cie_rp_callback")
@@ -89,11 +89,34 @@ def test_rp_callback_no_userinfo(self, mocked, mocked_2, mocked_3):
         self.assertTrue(res.status_code == 400)
         
     @override_settings(HTTP_CLIENT_SYNC=True)
-    @patch("spid_cie_oidc.relying_party.views.process_user_attributes", return_value=None)
-    @patch("spid_cie_oidc.relying_party.views.SpidCieOidcRpCallbackView.access_token_request", return_value=None)
+    @patch("spid_cie_oidc.relying_party.views.rp_callback.process_user_attributes", return_value=None)
+    @patch("spid_cie_oidc.relying_party.views.rp_callback.SpidCieOidcRpCallbackView.access_token_request", return_value=None)
     def test_rp_callback_no_token_response(self, mocked, mocked_2):
         client = Client()
         url = reverse("spid_cie_rp_callback")
         res = client.get(url, {"state": STATE, "code": CODE})
         self.assertTrue(res.status_code == 400)
         self.assertTrue("invalid token response" in res.content.decode())
+
+    @override_settings(HTTP_CLIENT_SYNC=True)
+    @patch("requests.post", return_value=MockedTokenEndPointResponse())
+    @patch("requests.get", return_value=MockedUserInfoResponse())
+    def test_rp_callback_no_rp_conf(self, mocked, mocked_2):
+        FederationEntityConfiguration.objects.filter(
+            sub = self.rp_config["sub"]
+        ).delete()
+        client = Client()
+        url = reverse("spid_cie_rp_callback")
+        res = client.get(url, {"state": STATE, "code": CODE})
+        self.assertTrue("Relay party not found" in res.content.decode())
+
+
+    @override_settings(HTTP_CLIENT_SYNC=True)
+    @patch("requests.post", return_value=MockedTokenEndPointResponse())
+    @patch("requests.get", return_value=MockedUserInfoResponse())
+    def test_rp_callback_no_authz(self, mocked, mocked_2):
+        OidcAuthentication.objects.all().delete()
+        client = Client()
+        url = reverse("spid_cie_rp_callback")
+        res = client.get(url, {"state": STATE, "code": CODE})
+        self.assertTrue("Authentication not found" in res.content.decode())
diff --git a/spid_cie_oidc/relying_party/tests/test_06_fetch_openid_providers.py b/spid_cie_oidc/relying_party/tests/test_06_fetch_openid_providers.py
index b0f07a8c..23a784ea 100644
--- a/spid_cie_oidc/relying_party/tests/test_06_fetch_openid_providers.py
+++ b/spid_cie_oidc/relying_party/tests/test_06_fetch_openid_providers.py
@@ -26,9 +26,8 @@ def create_tc():
     )
     return TrustChain.objects.create(
         sub=op_conf["sub"],
-        type="openid_provider",
         exp=EXP,
-        metadata=op_conf["metadata"]["openid_provider"],
+        metadata=op_conf["metadata"],
         status="valid",
         trust_anchor=ta_fes,
         is_active=True,
diff --git a/spid_cie_oidc/relying_party/tests/test_07_oidc_rp_landing.py b/spid_cie_oidc/relying_party/tests/test_07_oidc_rp_landing.py
index 33105161..5e7cd2b0 100644
--- a/spid_cie_oidc/relying_party/tests/test_07_oidc_rp_landing.py
+++ b/spid_cie_oidc/relying_party/tests/test_07_oidc_rp_landing.py
@@ -24,9 +24,8 @@ def setUp(self) -> None:
 
         self.trust_chain = TrustChain.objects.create(
             sub=op_conf["sub"],
-            type="openid_provider",
             exp=EXP,
-            metadata=op_conf["metadata"]["openid_provider"],
+            metadata=op_conf["metadata"],
             status="valid",
             trust_anchor=self.ta_fes,
             is_active=True,
diff --git a/spid_cie_oidc/relying_party_test/templates/op_user_staff_test.html b/spid_cie_oidc/relying_party_test/templates/op_user_staff_test.html
index d11948ac..38d99753 100644
--- a/spid_cie_oidc/relying_party_test/templates/op_user_staff_test.html
+++ b/spid_cie_oidc/relying_party_test/templates/op_user_staff_test.html
@@ -3,6 +3,22 @@
 {% load i18n %}
 {% load static %}
 
+{% block menu_links %}
+
+<li class="nav-item ">
+    <a class="nav-link {% if request.resolver_match.view_name == 'oidc_provider_staff_testing' %}active{% endif %}" 
+    href="{% url 'oidc_provider_staff_testing' %}">
+        <span>{% trans 'Home' %}</span>
+    </a>
+</li>
+<li class="nav-item">
+    <a class="nav-link {% if request.resolver_match.view_name == 'oidc_provider_access_history' %}active{% endif %}" 
+    href="{% url 'oidc_provider_access_history' %}">
+        <span>{% trans 'Access History' %}</span>
+    </a>
+</li>
+
+{% endblock menu_links %}
 {% block container %}
 <div>
     <div class="container pt-2 p-3">
diff --git a/spid_cie_oidc/relying_party_test/tests.py b/spid_cie_oidc/relying_party_test/tests.py
deleted file mode 100644
index 49290204..00000000
--- a/spid_cie_oidc/relying_party_test/tests.py
+++ /dev/null
@@ -1,2 +0,0 @@
-
-# Create your tests here.
diff --git a/docs/images/assigned_profile_op.png b/spid_cie_oidc/relying_party_test/tests/__init__.py
similarity index 100%
rename from docs/images/assigned_profile_op.png
rename to spid_cie_oidc/relying_party_test/tests/__init__.py
diff --git a/spid_cie_oidc/relying_party_test/tests/test_01_user_staff.py b/spid_cie_oidc/relying_party_test/tests/test_01_user_staff.py
new file mode 100644
index 00000000..eba43b81
--- /dev/null
+++ b/spid_cie_oidc/relying_party_test/tests/test_01_user_staff.py
@@ -0,0 +1,72 @@
+from django.contrib.auth import get_user_model
+from django.test import Client, TestCase
+from django.urls import reverse
+from spid_cie_oidc.authority.tests.settings import rp_conf
+from spid_cie_oidc.entity.models import FederationEntityConfiguration
+from spid_cie_oidc.provider.models import OidcSession
+from spid_cie_oidc.provider.tests.settings import op_conf
+
+
+class RPTetstTest(TestCase):
+
+    def setUp(self):
+        self.user = get_user_model().objects.create(
+            username="test",
+            first_name="test",
+            last_name="test",
+            email="test@test.it",
+            is_staff = True
+        )
+        self.user.set_password("test")
+        self.user.save()
+        OidcSession.objects.create(
+            user=self.user,
+            user_uid="",
+            nonce="",
+            authz_request={
+                "scope": "openid", 
+                "nonce": "123", 
+                "acr_values" : ["https://www.spid.gov.it/SpidL2"],            
+                "redirect_uri" : rp_conf["metadata"]["openid_relying_party"]["redirect_uris"][0],
+                "state" : "state",
+            },
+            client_id="",
+            auth_code="code",
+        )
+        self.op_conf = FederationEntityConfiguration.objects.create(**op_conf)
+
+
+    def test_user_staff_get(self):
+        c = Client()
+        c.login(username="test", password="test")
+        session = c.session
+        session.update({"oidc": {"auth_code": "code"}})
+        session.save()
+        url = reverse("oidc_provider_staff_testing")
+        res = c.get(url)
+        self.assertTrue("test page" in res.content.decode())
+
+    def test_user_staff_post(self):
+        c = Client()
+        c.login(username="test", password="test")
+        session = c.session
+        session.update({"oidc": {"auth_code": "code"}})
+        session.save()
+        url = reverse("oidc_provider_staff_testing")
+        res = c.post(url)
+        self.assertTrue(res.status_code == 302)
+        self.assertTrue("callback" in res.url)
+
+    def test_user_staff_get_no_session(self):
+        c = Client()
+        c.login(username="test", password="test")
+        url = reverse("oidc_provider_staff_testing")
+        res = c.get(url)
+        self.assertTrue(res.status_code == 403)
+
+    def test_user_staff_post_no_session(self):
+        c = Client()
+        c.login(username="test", password="test")
+        url = reverse("oidc_provider_staff_testing")
+        res = c.post(url)
+        self.assertTrue(res.status_code == 403)