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

Issue:
fixes for httpd connections parameters and jwks evaluation and added openid_credential_issuer



Discussion / hints:

Fix patch:
diff --git a/examples/federation_authority/federation_authority/settingslocal.py.example b/examples/federation_authority/federation_authority/settingslocal.py.example
index 767168cd..f8d2696c 100644
--- a/examples/federation_authority/federation_authority/settingslocal.py.example
+++ b/examples/federation_authority/federation_authority/settingslocal.py.example
@@ -30,6 +30,11 @@ OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000"
 
 OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR]
 
+OIDCFED_PROVIDER_PROFILE = "spid"
+OIDCFED_PROVIDER_MAX_REFRESH = 10 #used in SPID
+
+# OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME = 3600 #used in CIE (seconds)
+
 # for RP only
 OIDCFED_IDENTITY_PROVIDERS = {
   "spid": {
diff --git a/examples/provider/provider/settingslocal.py.example b/examples/provider/provider/settingslocal.py.example
index 118bfed0..5b5ec6d5 100644
--- a/examples/provider/provider/settingslocal.py.example
+++ b/examples/provider/provider/settingslocal.py.example
@@ -19,8 +19,10 @@ APPEND_SLASH = False
 # required for onboarding checks and also for all the leafs
 OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000"
 OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR]
-OIDCFED_PROVIDER_PROFILE = "cie"
- 
+OIDCFED_PROVIDER_PROFILE = "spid"
+#OIDCFED_PROVIDER_MAX_REFRESH = 10 #used in SPID
+OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME = 3600 #used in CIE (seconds)
+
 OIDCFED_REQUIRED_TRUST_MARKS = []
 
 #OIDCFED_FEDERATION_TRUST_MARKS_PROFILES = {
diff --git a/examples/wallet_trust_anchor/templates/onboarding_landing.html b/examples/wallet_trust_anchor/templates/onboarding_landing.html
index 31abedcc..bb94de8e 100644
--- a/examples/wallet_trust_anchor/templates/onboarding_landing.html
+++ b/examples/wallet_trust_anchor/templates/onboarding_landing.html
@@ -10,23 +10,6 @@
        <div class="d-lg-flex">
             <div class="col-12">
                 <div class="row">
-                    <div class="card-wrapper card-space col-6">
-                        <div class="card card-bg no-after">
-                            <div class="card-body pl-lg-0">
-                                <div class="h-100">
-                                    <div class="col-12">
-                                        <div class="img-responsive-wrapper d-none d-lg-block h-100">
-                                            <div class="img-responsive img-responsive-panoramic h-100 ml-4 mt-2">
-                                                <figure class="img-wrapper">
-                                                    <img alt="superman" class="h-100" src="{% static 'images/SPID-e-CIE-696x383.jpg' %}" title="spid-cie">
-                                                </figure>
-                                            </div>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
                     <div class="card-wrapper card-space col-6">
                         <div class="card card-bg no-after">
                             <div class="card-body pl-lg-0">
diff --git a/examples/wallet_trust_anchor/wallet_trust_anchor/settingslocal.py.example b/examples/wallet_trust_anchor/wallet_trust_anchor/settingslocal.py.example
index e97dd13a..3e545e67 100644
--- a/examples/wallet_trust_anchor/wallet_trust_anchor/settingslocal.py.example
+++ b/examples/wallet_trust_anchor/wallet_trust_anchor/settingslocal.py.example
@@ -38,8 +38,12 @@ OIDCFED_FEDERATION_TRUST_MARKS_PROFILES = {
 }
 
 HTTPC_PARAMS = {
-    "connection": {"ssl": True},
-    "session": {"timeout": aiohttp.ClientTimeout(total=4)},
+    # enable/disable the connection params to avoid "Failed to fetch Entity Configuration -> ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:997)')]
+    # see: https://docs.aiohttp.org/en/v3.7.3/client_reference.html
+    # "connection": {"ssl": True, "verify_ssl": False},
+    "session": {
+        "timeout": aiohttp.ClientTimeout(total=4),
+    },
 }
 
 FEDERATION_DEFAULT_POLICY = {
diff --git a/spid_cie_oidc/__init__.py b/spid_cie_oidc/__init__.py
index 5becc17c..6849410a 100644
--- a/spid_cie_oidc/__init__.py
+++ b/spid_cie_oidc/__init__.py
@@ -1,1 +1,1 @@
-__version__ = "1.0.0"
+__version__ = "1.1.0"
diff --git a/spid_cie_oidc/authority/admin.py b/spid_cie_oidc/authority/admin.py
index 811f5954..60e1eff5 100644
--- a/spid_cie_oidc/authority/admin.py
+++ b/spid_cie_oidc/authority/admin.py
@@ -23,7 +23,7 @@ class FederationDescendantAdmin(admin.ModelAdmin):
     readonly_fields = (
         "created",
         "modified",
-        "entity_statement_as_json",
+        "entity_statement_preview",
     )
     inlines = (
         FederationDescendantContactAdminInline,
diff --git a/spid_cie_oidc/authority/models.py b/spid_cie_oidc/authority/models.py
index cafe4744..38b3f033 100644
--- a/spid_cie_oidc/authority/models.py
+++ b/spid_cie_oidc/authority/models.py
@@ -175,7 +175,7 @@ def entity_profiles(self):
     def entity_statement_as_dict(self, iss: str = None, aud: list = None) -> dict:
 
         policies = {
-            k: FEDERATION_DEFAULT_POLICY[k] for k in self.entity_profiles
+            k: FEDERATION_DEFAULT_POLICY.get(k, {}) for k in self.entity_profiles
         }
 
         # apply custom policies if defined
@@ -188,10 +188,10 @@ def entity_statement_as_dict(self, iss: str = None, aud: list = None) -> dict:
             "sub": self.sub,
             "jwks": {"keys": self.jwks}
         }
-        
+
         if policies:
             data["metadata_policy"] = policies
-        
+
         if ta.fetch_endpoint:
             data["source_endpoint"] = ta.fetch_endpoint
 
@@ -224,6 +224,9 @@ def entity_statement_as_dict(self, iss: str = None, aud: list = None) -> dict:
     def entity_statement_as_json(self, iss: str = None, aud: list = None) -> str:
         return json.dumps(self.entity_statement_as_dict(iss, aud))
 
+    def entity_statement_preview(self):
+        return self.entity_statement_as_json()
+
     def entity_statement_as_jws(self, iss: str = None, aud: list = None) -> str:
         issuer = get_first_self_trust_anchor(iss)
         return create_jws(
diff --git a/spid_cie_oidc/authority/urls.py b/spid_cie_oidc/authority/urls.py
index 56f411f6..270f44c7 100644
--- a/spid_cie_oidc/authority/urls.py
+++ b/spid_cie_oidc/authority/urls.py
@@ -16,7 +16,13 @@
 from django.conf import settings
 from django.urls import path
 
-from .views import entity_list, fetch, trust_mark_status, advanced_entity_listing
+from .views import (
+    entity_list,
+    fetch,
+    trust_mark_status,
+    advanced_entity_listing,
+    trust_marked_list
+)
 
 _PREF = getattr(settings, "OIDC_PREFIX", "")
 urlpatterns = [
@@ -32,4 +38,5 @@
         advanced_entity_listing,
         name="oidcfed_advanced_entity_listing",
     ),
+    path(f"{_PREF}trust_marked_list", trust_marked_list, name="oidcfed_tm_list"),
 ]
diff --git a/spid_cie_oidc/authority/views.py b/spid_cie_oidc/authority/views.py
index fe48a093..eb4f3bb1 100644
--- a/spid_cie_oidc/authority/views.py
+++ b/spid_cie_oidc/authority/views.py
@@ -111,6 +111,31 @@ def entity_list(request):
     return JsonResponse(list(set(entries)), safe=False)
 
 
+# TODO - add the schema
+# @schema(
+    # methods=['GET'],
+    # get_request_schema = {
+    # "application/x-www-form-urlencoded": ListRequest
+    # },
+    # get_response_schema = {
+    # "400": FedAPIErrorResponse,
+    # "404": FedAPIErrorResponse,
+    # "200": ListResponse
+    # },
+    # tags = ['Federation API']
+# )
+def trust_marked_list(request):
+    if request.GET.get("trust_mark_id", "").lower():
+        _q = {"profile__profile_id": request.GET["trust_mark_id"]}
+    else:
+        _q = {}
+
+    entries = FederationEntityAssignedProfile.objects.filter(**_q).values_list(
+        "descendant__sub", flat=True
+    )
+    return JsonResponse(list(set(entries)), safe=False)
+
+
 @schema(
     methods=['GET'],
     get_request_schema = {
diff --git a/spid_cie_oidc/entity/http_client.py b/spid_cie_oidc/entity/http_client.py
index 0f3fd142..152fa0d7 100644
--- a/spid_cie_oidc/entity/http_client.py
+++ b/spid_cie_oidc/entity/http_client.py
@@ -20,7 +20,11 @@ async def fetch_all(session, urls, httpc_params):
 
 
 async def http_get(urls, httpc_params: dict = {}):
-    async with aiohttp.ClientSession(**httpc_params.get("session", {})) as session:
+    _con = aiohttp.TCPConnector(**httpc_params.get("connection", {}))
+    async with aiohttp.ClientSession(
+            connector=_con,
+            **httpc_params.get("session", {})
+    ) as session:
         text = await fetch_all(session, urls, httpc_params)
         return text
 
diff --git a/spid_cie_oidc/entity/jwtse.py b/spid_cie_oidc/entity/jwtse.py
index 95714d8e..f5d40818 100644
--- a/spid_cie_oidc/entity/jwtse.py
+++ b/spid_cie_oidc/entity/jwtse.py
@@ -50,12 +50,12 @@ def create_jwe(plain_dict: Union[dict, str, int, None], jwk_dict: dict, **kwargs
     if isinstance(plain_dict, dict):
         _payload = json.dumps(plain_dict).encode()
     elif not plain_dict:
-        logger.warning(f"create_jwe with null payload!")
+        logger.warning("create_jwe with null payload!")
         _payload = ""
     elif isinstance(plain_dict, (str, int)):
         _payload = plain_dict
     else:
-        logger.error(f"create_jwe with unsupported payload type!")
+        logger.error("create_jwe with unsupported payload type!")
         _payload = ""
 
     _keyobj = JWE_CLASS(
diff --git a/spid_cie_oidc/entity/settings.py b/spid_cie_oidc/entity/settings.py
index 5bf1379c..3708c727 100644
--- a/spid_cie_oidc/entity/settings.py
+++ b/spid_cie_oidc/entity/settings.py
@@ -74,8 +74,9 @@
 FEDERATION_DEFAULT_EXP = getattr(settings, "FEDERATION_DEFAULT_EXP", 2880)
 
 ENTITY_TYPE_LEAFS = [
-    "openid_relying_party", 
-    "openid_provider", 
+    "openid_relying_party",
+    "openid_provider",
+    "openid_credential_issuer",
     "oauth_resource",
     "wallet_provider",
     "wallet_relying_party"
diff --git a/spid_cie_oidc/entity/statements.py b/spid_cie_oidc/entity/statements.py
index d72ee03b..3f6ee788 100644
--- a/spid_cie_oidc/entity/statements.py
+++ b/spid_cie_oidc/entity/statements.py
@@ -149,7 +149,7 @@ def __init__(
         self.sub = self.payload["sub"]
         self.iss = self.payload["iss"]
         self.jwks = get_federation_jwks(self.payload, httpc_params)
-        if not self.jwks[0]:
+        if not self.jwks or not self.jwks[0]:
             _msg = f"Missing jwks in the statement for {self.sub}"
             logger.error(_msg)
             raise MissingJwksClaim(_msg)
diff --git a/spid_cie_oidc/entity/validators.py b/spid_cie_oidc/entity/validators.py
index 26b00c0c..fc80c357 100644
--- a/spid_cie_oidc/entity/validators.py
+++ b/spid_cie_oidc/entity/validators.py
@@ -88,7 +88,7 @@ def validate_entity_metadata(value):
             raise ValidationError(
                 f"RP metadata fail {e}. "
             )
-    
+
     # TODO - add wallet_provider and wallet_relying_party once standardized
 
 
diff --git a/spid_cie_oidc/entity/views.py b/spid_cie_oidc/entity/views.py
index 647cda3f..2aa3a844 100644
--- a/spid_cie_oidc/entity/views.py
+++ b/spid_cie_oidc/entity/views.py
@@ -80,7 +80,6 @@ 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.")
 
diff --git a/spid_cie_oidc/entity/x509.py b/spid_cie_oidc/entity/x509.py
index 725d50ba..9282c961 100644
--- a/spid_cie_oidc/entity/x509.py
+++ b/spid_cie_oidc/entity/x509.py
@@ -1,5 +1,3 @@
-import datetime
-import os
 
 from cryptography import x509
 from cryptography.x509.oid import NameOID
diff --git a/spid_cie_oidc/onboarding/templates/onboarding_resolve_statement.html b/spid_cie_oidc/onboarding/templates/onboarding_resolve_statement.html
index 65506c20..1826009d 100644
--- a/spid_cie_oidc/onboarding/templates/onboarding_resolve_statement.html
+++ b/spid_cie_oidc/onboarding/templates/onboarding_resolve_statement.html
@@ -27,6 +27,7 @@ <h4 class="text-left">
                 </option>
                 {% endfor %}
             </select>
+            <span for="id_{{ form_field.html_name }}" class="text-danger">{{ form_field.errors }}</span>
         </div>
 
         {% else %}
@@ -55,4 +56,4 @@ <h4 class="text-left">
         <input type="submit" name="confirm" class="btn btn-lg btn-success btn-block" value="{% trans 'resolve' %}" />
     </form>
 </div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/spid_cie_oidc/onboarding/views.py b/spid_cie_oidc/onboarding/views.py
index d8f7872b..6bf7bbee 100644
--- a/spid_cie_oidc/onboarding/views.py
+++ b/spid_cie_oidc/onboarding/views.py
@@ -152,7 +152,6 @@ def onboarding_resolve_statement(request):
     else:
         form = OnboardingCreateTrustChain()
     context = {'form': form}
-
     if form.is_valid():
         context = {
             'form': form,
@@ -164,6 +163,7 @@ def onboarding_resolve_statement(request):
             context["resolved_statement"] = json.dumps(resultJson, indent=4)
         except Exception:
             messages.error(request, _('Failed to resolve entity statement, Please check your inserted data'))
+    
     return render(request, 'onboarding_resolve_statement.html', context)
 
 
@@ -286,7 +286,7 @@ def onboarding_decode_jwt(request):
             if not form_dict.get('jwk'):
                 messages.error(
                     request,
-                    f"JWE needs a private jwk to be decrypted"
+                    "JWE needs a private jwk to be decrypted"
                 )
                 return render(
                     request, "onboarding_decode_jwt.html", context, status = 400
diff --git a/spid_cie_oidc/provider/settings.py b/spid_cie_oidc/provider/settings.py
index df842fdb..bf2f11d9 100644
--- a/spid_cie_oidc/provider/settings.py
+++ b/spid_cie_oidc/provider/settings.py
@@ -11,9 +11,11 @@
     AuthenticationRequestDoc,
     AuthenticationRequestSpid
 )
-from spid_cie_oidc.provider.schemas.authn_response import AuthenticationErrorResponse, AuthenticationErrorResponseCie, AuthenticationResponse, AuthenticationResponseCie
+from spid_cie_oidc.provider.schemas.authn_response import AuthenticationErrorResponse, AuthenticationErrorResponseCie, \
+    AuthenticationResponse, AuthenticationResponseCie
 from spid_cie_oidc.provider.schemas.introspection_request import IntrospectionRequest
-from spid_cie_oidc.provider.schemas.introspection_response import IntrospectionErrorResponseCie, IntrospectionErrorResponseSpid, IntrospectionResponse
+from spid_cie_oidc.provider.schemas.introspection_response import IntrospectionErrorResponseCie, \
+    IntrospectionErrorResponseSpid, IntrospectionResponse
 from spid_cie_oidc.provider.schemas.revocation_request import RevocationRequest
 from spid_cie_oidc.provider.schemas.revocation_response import RevocationErrorResponse
 from spid_cie_oidc.provider.schemas.token_requests import TokenAuthnCodeRequest, TokenRefreshRequest
@@ -142,6 +144,11 @@
     10
 )
 
+OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME = getattr(
+    settings,
+    "OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME",
+    2
+)
 
 OIDCFED_PROVIDER_PROFILES_DEFAULT_ACR = getattr(
     settings,
diff --git a/spid_cie_oidc/provider/static/svg/spid-logo-c-lb.svg-LTS.svg b/spid_cie_oidc/provider/static/svg/spid-logo-c-lb.svg-LTS.svg
new file mode 100644
index 00000000..70d2caa2
--- /dev/null
+++ b/spid_cie_oidc/provider/static/svg/spid-logo-c-lb.svg-LTS.svg
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   id="Layer_1"
+   viewBox="0 0 143 69.9"
+   version="1.1"
+   sodipodi:docname="spid-logo-c-lb.svg-LTS.svg"
+   inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs12" />
+  <sodipodi:namedview
+     id="namedview10"
+     pagecolor="#ffffff"
+     bordercolor="#000000"
+     borderopacity="0.25"
+     inkscape:showpageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     showgrid="false"
+     inkscape:zoom="3.3762517"
+     inkscape:cx="65.753392"
+     inkscape:cy="39.244704"
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="Layer_1" />
+  <style
+     id="style2">.st0{fill:#06C}</style>
+  <path
+     id="XMLID_85_"
+     class="st0"
+     d="M29.8 25c-6.3-.8-10.8-1.2-13.3-1.2s-4.2.2-4.9.7c-.7.5-1.1 1.2-1.1 2.2 0 1 .5 1.7 1.5 2.1 1 .4 3.6.9 7.7 1.7 4.1.7 7.1 2 8.8 3.6 1.7 1.7 2.6 4.4 2.6 8.2 0 8.3-5.1 12.4-15.4 12.4-3.4 0-7.5-.5-12.3-1.4l-2.5-.5.3-8.6c6.3.8 10.8 1.2 13.2 1.2 2.5 0 4.2-.2 5.1-.7.9-.5 1.3-1.2 1.3-2.2 0-1-.5-1.7-1.4-2.1-.9-.5-3.4-1-7.4-1.7s-6.9-1.8-8.9-3.4C1.2 33.7.2 30.9.2 26.8s1.4-7.1 4.1-9.2c2.8-2.1 6.3-3.1 10.7-3.1 3 0 7.1.5 12.4 1.5l2.5.5-.1 8.5z" />
+  <path
+     id="XMLID_82_"
+     class="st0"
+     d="M55.4 23.7c-1.9 0-3.8.4-5.8 1.2l-.9.4v20.1c2.3.3 4.2.5 5.6.5 3 0 5-.9 6.1-2.6 1.1-1.7 1.7-4.7 1.7-8.9 0-7.2-2.2-10.7-6.7-10.7M38.5 69.9V15.4h10.2v2.1c3.3-2 6.2-3 8.8-3 5.2 0 9 1.6 11.4 4.6 2.5 3.1 3.7 8.4 3.7 15.8s-1.4 12.6-4.1 15.5c-2.7 2.9-7.2 4.3-13.4 4.3-1.7 0-3.5-.2-5.5-.5l-.9-.2v15.8H38.5z" />
+  <path
+     id="XMLID_79_"
+     class="st0"
+     d="M131.6 44.5l1.1-.2V24.1c-2.8-.5-5.3-.8-7.5-.8-4.1 0-6.2 3.7-6.2 11.1 0 4 .5 6.8 1.4 8.5.9 1.7 2.4 2.5 4.6 2.5s4.3-.2 6.6-.9M143 0v53.8h-10.2v-1.6c-3.6 1.7-6.7 2.5-9.3 2.5-5.6 0-9.5-1.6-11.7-4.8-2.2-3.2-3.3-8.3-3.3-15.3s1.3-12.1 4-15.3c2.6-3.2 6.6-4.8 11.9-4.8 1.6 0 3.9.3 6.8.8l1.5.3V0H143z" />
+  <path
+     id="XMLID_78_"
+     class="st0"
+     d="M90.5 37.7c-3.2 0-5.8-1.1-7.8-3.2s-3.1-4.8-3.1-8 1-5.8 3.1-7.9c2-2.1 4.6-3.1 7.8-3.1s5.8 1.1 7.7 3.2c2 2.2 3 4.8 3 8s-1 5.8-3 7.9c-2 2-4.5 3.1-7.7 3.1" />
+  <path
+     id="XMLID_77_"
+     class="st0"
+     d="M79.6 53.2c0-3.2 1-5.8 3.1-7.9 2-2.1 4.6-3.1 7.8-3.1s5.8 1.1 7.7 3.2c2 2.2 3 4.8 3 8" />
+  <text
+     xml:space="preserve"
+     style="fill:#ff0000"
+     x="5.6275425"
+     y="66.938133"
+     id="text172"><tspan
+       sodipodi:role="line"
+       id="tspan170"
+       x="5.6275425"
+       y="66.938133" /></text>
+  <text
+     xml:space="preserve"
+     style="fill:#ff0000"
+     x="109.88517"
+     y="97.149155"
+     id="text280"><tspan
+       sodipodi:role="line"
+       id="tspan278"
+       x="109.88517"
+       y="97.149155" /></text>
+  <text
+     xml:space="preserve"
+     style="fill:#ff0000"
+     x="36.727119"
+     y="89.152122"
+     id="text284"><tspan
+       sodipodi:role="line"
+       id="tspan282"
+       x="36.727119"
+       y="89.152122" /></text>
+  <text
+     xml:space="preserve"
+     style="fill:#ff0000"
+     x="33.46907"
+     y="85.597885"
+     id="text288"><tspan
+       sodipodi:role="line"
+       id="tspan290"
+       x="33.46907"
+       y="85.597885" /></text>
+  <text
+     xml:space="preserve"
+     style="font-size:26.8362px;fill:#ff0000;stroke-width:2.23637"
+     x="-0.47511318"
+     y="21.411407"
+     id="text285"
+     transform="scale(1.2833473,0.77921228)"><tspan
+       sodipodi:role="line"
+       id="tspan291"
+       style="font-weight:bold;stroke-width:2.23637"
+       x="-0.47511318"
+       y="21.411407">   LONG</tspan><tspan
+       sodipodi:role="line"
+       id="tspan293"
+       style="font-weight:bold;stroke-width:2.23637"
+       x="-0.47511318"
+       y="54.956661">   TERM</tspan><tspan
+       sodipodi:role="line"
+       id="tspan295"
+       style="font-weight:bold;stroke-width:2.23637"
+       x="-0.47511318"
+       y="88.501915">SESSION</tspan></text>
+</svg>
diff --git a/spid_cie_oidc/provider/views/__init__.py b/spid_cie_oidc/provider/views/__init__.py
index 05bcdc05..76ec28ac 100644
--- a/spid_cie_oidc/provider/views/__init__.py
+++ b/spid_cie_oidc/provider/views/__init__.py
@@ -238,7 +238,6 @@ def validate_json_schema(self, payload, schema_type, error_description):
 
     def get_jwt_common_data(self):
         return {
-            "jti": str(uuid.uuid4()),
             "exp": exp_from_now(),
             "iat": iat_now()
         }
@@ -253,6 +252,7 @@ def get_access_token(
             "aud": [authz.client_id],
             "client_id": authz.client_id,
             "scope": authz.authz_request["scope"],
+            "jti": str(uuid.uuid4())
         }
         access_token.update(commons)
 
@@ -293,7 +293,8 @@ def get_id_token(
             "c_hash": left_hash(authz.auth_code, "HS256"),
             "aud": [authz.client_id],
             "iss": iss_sub,
-            "acr": authz.acr
+            "acr": authz.acr,
+            "jti": str(uuid.uuid4())
         }
         claims = self.get_id_token_claims(authz)
         if claims:
@@ -324,6 +325,7 @@ def get_refresh_token(
                 "c_hash": left_hash(authz.auth_code, "HS256"),
                 "aud": authz.client_id,
                 "iss": iss_sub,
+                "jti": str(uuid.uuid4())
             }
             refresh_token.update(commons)
             return refresh_token
diff --git a/spid_cie_oidc/provider/views/token_endpoint.py b/spid_cie_oidc/provider/views/token_endpoint.py
index 9fe0a13b..cc098d2f 100644
--- a/spid_cie_oidc/provider/views/token_endpoint.py
+++ b/spid_cie_oidc/provider/views/token_endpoint.py
@@ -1,4 +1,3 @@
-
 import base64
 import hashlib
 import logging
@@ -20,33 +19,35 @@
 from spid_cie_oidc.provider.models import IssuedToken, OidcSession
 from spid_cie_oidc.provider.settings import (
     OIDCFED_DEFAULT_PROVIDER_PROFILE,
-    OIDCFED_PROVIDER_PROFILES
+    OIDCFED_PROVIDER_PROFILES,
+    OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME
 )
 
+from spid_cie_oidc.entity.utils import iat_now
 from . import OpBase
-logger = logging.getLogger(__name__)
 
+logger = logging.getLogger(__name__)
 
 schema_profile = OIDCFED_PROVIDER_PROFILES[OIDCFED_DEFAULT_PROVIDER_PROFILE]
 
 
 @schema(
-    methods=['GET','POST'],
-    post_request_schema = {
+    methods=['GET', 'POST'],
+    post_request_schema={
         "authn_request": schema_profile["authorization_code"],
         "refresh_request": schema_profile["refresh_token"],
 
     },
-    post_response_schema = {
-            "200": schema_profile["authorization_code_response"],
-            # TODO
-            # "200": schema_profile["refresh_token_response"],
-            "400": schema_profile["token_error_response"],
+    post_response_schema={
+        "200": schema_profile["authorization_code_response"],
+        # TODO
+        # "200": schema_profile["refresh_token_response"],
+        "400": schema_profile["token_error_response"],
     },
-    get_response_schema = {
-            "400": BaseModel
+    get_response_schema={
+        "400": BaseModel
     },
-    tags = ['Provider']
+    tags=['Provider']
 )
 @method_decorator(csrf_exempt, name="dispatch")
 class TokenEndpoint(OpBase, View):
@@ -77,35 +78,50 @@ def grant_auth_code(self, request, *args, **kwargs):
         #
 
         issued_token = IssuedToken.objects.filter(
-            session= self.authz,
-            revoked = False
+            session=self.authz,
+            revoked=False
         ).first()
 
         jwk_at = unpad_jwt_payload(issued_token.access_token)
         expires_in = self.get_expires_in(jwk_at['iat'], jwk_at['exp'])
 
-        iss_token_data = dict( # nosec B106
-            access_token = issued_token.access_token,
-            id_token = issued_token.id_token,
-            token_type = "Bearer", # nosec B106
-            expires_in = expires_in,
+        iss_token_data = dict(  # nosec B106
+            access_token=issued_token.access_token,
+            id_token=issued_token.id_token,
+            token_type="Bearer",  # nosec B106
+            expires_in=expires_in,
             # TODO: remove unsupported scope
-            scope = self.authz.authz_request["scope"],
+            scope=self.authz.authz_request["scope"],
         )
         if issued_token.refresh_token:
             iss_token_data['refresh_token'] = issued_token.refresh_token
         return JsonResponse(iss_token_data)
 
     def is_token_renewable(self, session) -> bool:
-        issuedToken = IssuedToken.objects.filter(
-            session = session
-        )
-        # TODO: check also ACR
-        return (
-            (issuedToken.count() - 1) < getattr(
-                settings, "OIDCFED_PROVIDER_MAX_REFRESH", 1
+        provider = getattr(settings, "OIDCFED_PROVIDER_PROFILE")
+        if provider == "cie":
+            issuedToken = IssuedToken.objects.filter(
+                session=session
+            ).first()
+
+            id_token = unpad_jwt_payload(issuedToken.id_token)
+
+            consent_expiration = id_token['iat'] + OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME
+
+            delta = consent_expiration - iat_now()
+            if delta > 0:
+                return True
+            return False
+        elif provider == "spid":
+            # TODO: check also ACR
+            issuedToken = IssuedToken.objects.filter(
+                session=session
+            )
+            return (
+                    (issuedToken.count() - 1) < getattr(
+                        settings, "OIDCFED_PROVIDER_MAX_REFRESH", 1
+                    )
             )
-        )
 
     def grant_refresh_token(self, request, *args, **kwargs):
         """
@@ -119,8 +135,8 @@ def grant_refresh_token(self, request, *args, **kwargs):
         # 2. create a new instance of issuedtoken linked to the same sessions and revoke the older
         # 3. response with a new refresh, access and id_token
         issued_token = IssuedToken.objects.filter(
-            refresh_token = request.POST['refresh_token'],
-            revoked = False
+            refresh_token=request.POST['refresh_token'],
+            revoked=False
         ).first()
 
         if not issued_token:
@@ -130,17 +146,17 @@ def grant_refresh_token(self, request, *args, **kwargs):
                     "error_description": "Refresh token not found",
 
                 },
-                status = 400
+                status=400
             )
 
         session = issued_token.session
-        if not self.is_token_renewable(session): # pragma: no cover
+        if not self.is_token_renewable(session):  # pragma: no cover
             return JsonResponse(
-                    {
-                        "error": "invalid_request",
-                        "error_description": "Refresh Token can no longer be updated",
+                {
+                    "error": "invalid_request",
+                    "error_description": "Refresh Token can no longer be updated",
 
-                    }, status = 400
+                }, status=400
             )
         iss_token_data = self.get_iss_token_data(session, self.get_issuer())
         IssuedToken.objects.create(**iss_token_data)
@@ -150,12 +166,12 @@ def grant_refresh_token(self, request, *args, **kwargs):
         jwk_at = unpad_jwt_payload(iss_token_data['access_token'])
         expires_in = self.get_expires_in(jwk_at['iat'], jwk_at['exp'])
 
-        data = dict( # nosec B106
-            access_token = iss_token_data['access_token'],
-            id_token = iss_token_data['id_token'],
-            refresh_token = iss_token_data['refresh_token'],
-            token_type = "Bearer", # nosec B106
-            expires_in = expires_in,
+        data = dict(  # nosec B106
+            access_token=iss_token_data['access_token'],
+            id_token=iss_token_data['id_token'],
+            refresh_token=iss_token_data['refresh_token'],
+            token_type="Bearer",  # nosec B106
+            expires_in=expires_in,
         )
 
         return JsonResponse(data)
@@ -174,7 +190,7 @@ def post(self, request, *args, **kwargs):
                     "error": "invalid_request",
                     "error_description": "Token request object validation failed",
                 },
-                status = 400
+                status=400
             )
 
         self.commons = self.get_jwt_common_data()
@@ -186,7 +202,7 @@ def post(self, request, *args, **kwargs):
                 request.POST['client_id'],
                 request.POST['client_assertion']
             )
-        except Exception as e: # pragma: no cover
+        except Exception as e:  # pragma: no cover
             logger.warning(
                 "Client authentication failed for "
                 f"{request.POST.get('client_id', 'unknown')}: {e}"
@@ -197,7 +213,7 @@ def post(self, request, *args, **kwargs):
                     'error': "unauthorized_client",
                     'error_description': ""
 
-                }, status = 403
+                }, status=403
             )
 
         if request.POST.get("grant_type") == 'authorization_code':
diff --git a/spid_cie_oidc/relying_party/static/svg/cie-ico-circle-bb-LTS.svg b/spid_cie_oidc/relying_party/static/svg/cie-ico-circle-bb-LTS.svg
new file mode 100644
index 00000000..45fe1935
--- /dev/null
+++ b/spid_cie_oidc/relying_party/static/svg/cie-ico-circle-bb-LTS.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1080" height="1080" viewBox="0 0 1080 1080" xml:space="preserve">
+<desc>Created with Fabric.js 5.2.4</desc>
+<defs>
+</defs>
+<g transform="matrix(1 0 0 1 540 540)" id="91fda1b0-16a7-4218-be94-fe968aea9287"  >
+</g>
+<g transform="matrix(1 0 0 1 540 540)" id="bf67b2b1-57ff-4e67-a18b-9056d38bc3b7"  >
+<rect style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1; visibility: hidden;" vector-effect="non-scaling-stroke"  x="-540" y="-540" rx="0" ry="0" width="1080" height="1080" />
+</g>
+<g transform="matrix(1 0 0 1 540 540)"  >
+<g style="" vector-effect="non-scaling-stroke"   >
+		<g transform="matrix(1 0 0 1 15.3 0)"  >
+<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke"  transform=" translate(-65.81, -54.25)" d="M 30.6 15.7 L 34 14.5 L 75.5 0.2 C 76.56540168959296 -0.18066706462342558 77.75357230207264 0.04930144101780526 78.6 0.8000000000000014 C 79.45278343969967 1.5639096924539222 80.10517684036854 2.5253315460712047 80.5 3.599999999999998 C 81.7 6.699999999999999 82.9 9.7 84.1 12.9 C 85.10490287348861 15.753272139425885 85.51333420425384 18.782471175934674 85.3 21.799999999999997 C 85.2 23.1 85 24.400000000000002 85 25.7 C 84.8997487421324 26.69748742132399 84.8997487421324 27.70251257867601 85 28.7 C 85.1 30 85.2 31.3 85.4 32.6 C 85.6679127655581 35.17784232706553 86.6362237855053 37.63320241336018 88.2 39.7 C 91 43.300000000000004 93.7 47 96.5 50.7 C 97.91713033796104 52.6006439461041 99.18746959048559 54.6064427658797 100.29999999999998 56.699999999999996 C 100.72948626076172 57.46553485118747 100.96950576896607 58.32274738048874 101 59.199999999999996 C 101.07307811645249 59.956229957075166 100.93459406188998 60.71789225716893 100.6 61.400000000000006 C 99.92601972444126 62.667018833488605 99.00846791093264 63.78847104999915 97.89999999999999 64.7 C 96.65622743722774 65.80166188924085 95.27873415273983 66.74238901035454 93.8 67.5 C 92.65744570589503 68.06025733600642 91.6376786331713 68.84207875842795 90.8 69.8 C 89.9521070517764 70.80275151344352 89.75747055840081 72.20413426574774 90.3 73.39999999999999 L 91.2 75.69999999999999 C 91.8554625718165 77.04584978347667 91.65969779024242 78.65112099238415 90.7 79.79999999999998 C 90.45738714699291 80.18357117568864 90.19015996918812 80.55100854517025 89.9 80.89999999999998 C 89.42865735261731 81.64291017503078 89.28398496528875 82.54711259583415 89.5 83.39999999999998 L 89.8 84.59999999999998 C 89.94813816233079 85.54363300569577 89.6529890347512 86.50286767032942 89 87.19999999999997 C 88.4 87.79999999999997 87.9 88.49999999999997 87.4 89.09999999999998 C 86.33883817925528 90.31118837502393 85.79963941681507 91.89283807818184 85.9 93.49999999999999 C 85.9938011077572 94.45296851707025 86.19508025227655 95.39227119149392 86.5 96.29999999999998 C 86.91857745940916 97.89773589887767 87.08724214884835 99.55064985538174 87 101.19999999999997 C 86.94617594664437 103.98514037610069 85.37295020253946 106.51765011051347 82.9 107.79999999999998 C 81.6565380495359 108.39508080499027 80.26333329885159 108.60406151759291 78.9 108.39999999999998 L 73.7 107.49999999999997 C 67.2 106.39999999999998 60.900000000000006 104.79999999999997 54.5 103.39999999999998 L 47.8 102 L 47.599999999999994 102 C 58.8 94.8 66.1 84.8 68.8 71.8 C 71.69842553866987 59.30491974682914 69.11447049705681 46.166782140881125 61.699999999999996 35.7 C 54.2 24.6 43.6 18.1 30.6 15.7 Z" stroke-linecap="round" />
+</g>
+		<g transform="matrix(1 0 0 1 -39.81 -13.45)"  >
+<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke"  transform=" translate(-10.7, -40.8)" d="M 12.2 57.5 C 7.3999999999999995 57.5 4.199999999999999 56.2 2.5 53.6 C 0.8000000000000007 51 0 46.7 0 40.7 C 0 34.7 0.9 30.4 2.6 27.9 C 4.3 25.4 7.5 24.099999999999998 12.2 24.099999999999998 C 15.299482037429783 24.099784037419465 18.387939251017634 24.469056095565843 21.400000000000013 25.200000000000006 L 21.4 29.5 C 18.56177516266078 28.98577132784907 15.684375758746704 28.718106267019856 12.799999999999944 28.700000000000003 C 10 28.7 8.099999999999998 29.599999999999998 7.099999999999999 31.3 C 6.1 33 5.499999999999998 36.1 5.499999999999998 40.8 C 5.499999999999998 45.49999999999999 5.999999999999998 48.599999999999994 6.999999999999998 50.3 C 7.999999999999998 52 9.899999999999999 52.8 12.7 52.8 C 15.614674853027882 52.81570359304371 18.525331068726384 52.58151286304498 21.400000000000006 52.099999999999994 L 21.4 56.49999999999999 C 18.377250101800986 57.15727407025505 15.293375109505583 57.49247787376541 12.199999999999996 57.49999999999999 Z" stroke-linecap="round" />
+</g>
+		<g transform="matrix(1 0 0 1 -22.41 -13.8)"  >
+<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke"  transform=" translate(-28.1, -40.45)" d="M 25.5 29.3 L 25.5 23.9 L 30.7 23.9 L 30.7 29.299999999999997 Z M 25.5 57 L 25.5 33.3 L 30.7 33.3 L 30.7 57 Z" stroke-linecap="round" />
+</g>
+		<g transform="matrix(1 0 0 1 -5.61 -9)"  >
+<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke"  transform=" translate(-44.9, -45.25)" d="M 52.9 52.7 L 54.199999999999996 52.7 L 54.199999999999996 56.5 C 51.04981609257554 57.216401107360326 47.830583889554504 57.58527146395648 44.599999999999994 57.599999999999994 C 41.099999999999994 57.6 38.599999999999994 56.7 37.099999999999994 54.7 C 35.599999999999994 52.7 34.8 49.7 34.8 45.5 C 34.8 37.1 38.199999999999996 32.9 45.099999999999994 32.9 C 51.99999999999999 32.9 54.99999999999999 36.5 54.99999999999999 43.8 L 54.699999999999996 47.4 L 40.1 47.4 C 40.01052016467688 48.97298018298103 40.470023658742875 50.528222778281304 41.4 51.8 C 42.199999999999996 52.699999999999996 43.699999999999996 53.099999999999994 46 53.099999999999994 Z M 50 43.4 C 50 41 49.7 39.4 48.9 38.5 C 48.099999999999994 37.6 46.9 37.1 45.199999999999996 37.1 C 43.75870688793964 36.98067792476598 42.33635041777776 37.491267426875375 41.3 38.5 C 40.5 39.5 40.099999999999994 41.1 40.099999999999994 43.4 Z" stroke-linecap="round" />
+</g>
+		<g transform="matrix(1 0 0 1 -27.66 25.25)"  >
+<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke"  transform=" translate(-22.85, -79.5)" d="M 20.2 95.7 L 20.2 63.3 L 25.5 63.3 L 25.5 95.7 Z" stroke-linecap="round" />
+</g>
+		<g transform="matrix(1 0 0 1 -7.5 25.25)"  >
+<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke"  transform=" translate(-43.01, -79.5)" d="M 42.2 95.7 L 30.9 95.7 L 30.9 63.3 L 42.2 63.3 C 47.1 63.3 50.6 64.5 52.400000000000006 67.1 C 54.20000000000001 69.69999999999999 55.10000000000001 73.6 55.10000000000001 79 C 55.16292964283061 81.3108982321395 54.995435555499796 83.62231663730473 54.60000000000001 85.89999999999999 C 54.25681263742343 87.72599061410322 53.61486768774912 89.48289258163291 52.7 91.10000000000001 C 50.9 94.2 47.4 95.7 42.2 95.7 Z M 49.7 79 C 49.7 75 49.2 72.2 48.2 70.5 C 47.2 68.8 45.2 67.9 42.2 67.9 L 36.2 67.9 L 36.2 91.1 L 42.2 91.1 C 45.300000000000004 91.1 47.400000000000006 89.89999999999999 48.400000000000006 87.6 C 48.94158950263057 86.43260723579563 49.27978654413 85.18127818224775 49.400000000000006 83.89999999999999 C 49.61863879768064 82.27609752559773 49.718903185302 80.63844586111547 49.699999999999996 78.99999999999999 Z" stroke-linecap="round" />
+</g>
+</g>
+</g>
+<g transform="matrix(1 0 0 1 540 608.33)" style="" id="49b1b3d3-ce32-41b6-88ff-dd8774c69031"  >
+		<text xml:space="preserve" font-family="Lato" font-size="20" font-style="normal" font-weight="400" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,19,0); fill-rule: nonzero; opacity: 1; white-space: pre;" ><tspan x="-80.28" y="6.28" >Long Term Session</tspan></text>
+</g>
+</svg>
\ No newline at end of file
diff --git a/spid_cie_oidc/relying_party/templates/echo_attributes.html b/spid_cie_oidc/relying_party/templates/echo_attributes.html
index 01bd8091..5242637c 100644
--- a/spid_cie_oidc/relying_party/templates/echo_attributes.html
+++ b/spid_cie_oidc/relying_party/templates/echo_attributes.html
@@ -42,7 +42,7 @@ <h4 class="text-left">
     </dl>
 
     <a href="{% url 'spid_cie_rpinitiated_logout' %}" class="btn btn-secondary" role="button">Log out</a>
-    <a href="{% url 'spid_cie_rp_echo_introspection' %}" class="btn btn-secondary" role="button">Verify Token (Introspection)</a>
+    <a href="{% url 'spid_cie_rp_echo_introspection' %}" class="btn btn-secondary" role="button">Verify Access Token (Introspection)</a>
 
 </div>
 {% if rt_expiration == 0 %}
@@ -51,18 +51,23 @@ <h4>{% trans "Warning" %}:</h4>
     <p>{% trans 'To enable revocable long sessions you must set <em>OIDCFED_ACR_PROFILES</em> in settingslocal.py of the Relying Party' %} <em>OIDCFED_ACR_PROFILES=["https://www.spid.gov.it/SpidL2","https://www.spid.gov.it/SpidL1"]</em></p>
 </div>
 {% else %}
-<div class="col-6 p-4">
+<div class="col-4 p-4">
         <h4 class="text-left">ACCESS TOKEN</h4>
-    <p> {% trans "The" %} access token <em>{{ rt_jti }}</em> <br />
+    <p> {% trans "The" %} access token <em>{{ at_jti }}</em> <br />
         {% trans "expires in" %} <span id="at_time">{{ at_expiration }}</span> </p>
 </div>
-<div class="col-6 p-4">
+<div class="col-8 p-4">
         <h4 class="text-left">REFRESH TOKEN <a href="{% url 'spid_cie_rp_extend_session' %}" class="btn btn-primary btn-xs btn-me" role="button">{% trans "Extend Session" %}</a></h4>
 
     <p> {% trans "The" %} Refresh Token <em>{{ rt_jti }}</em> <br />
         {% trans "expires in" %} <span id="rt_time">{{ rt_expiration }}</span> </p>
     <div class="alert alert-warning" role="alert">
-        <p>NB: {% trans "the number of Refresh Tokens that can be issued for a single session is set in the OPs configuration" %} <em>OIDCFED_PROVIDER_MAX_REFRESH</em>. {% trans "Past this number, the system returns error HTTP/400 and redirects to the landing page" %} </p>
+        <p style="text-align:justify;">NB:
+            <ul>
+                <li>{% trans "By selecting CIE PROVIDER, the Refresh Token is allowed to be used during the time frame validity of the consent given by the user and is set by the Provider with the variable" %} <em>OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME</em> {% trans "in settingslocal.py. Upon expiration the Provider returns error HTTP/400 and the RP example redirects to the landing page" %}</li>
+                <li>{% trans "By selecting SPID PROVIDER, the number of Refresh Tokens that can be issued for a single session is set in the OPs configuration" %} <em>OIDCFED_PROVIDER_MAX_REFRESH</em>. {% trans "Past this number, the Provider returns error HTTP/400 and the RP example redirects to the landing page" %} </li>
+            </ul>
+        </p>
     </div>
 </div>
 {% endif %}
diff --git a/spid_cie_oidc/relying_party/templates/rp_landing.html b/spid_cie_oidc/relying_party/templates/rp_landing.html
index 217ee3cb..0997295e 100644
--- a/spid_cie_oidc/relying_party/templates/rp_landing.html
+++ b/spid_cie_oidc/relying_party/templates/rp_landing.html
@@ -65,10 +65,9 @@ <h3 class="text-left">
                             </li>
                          <li>
                             <a href="{% url 'spid_cie_rp_begin' %}?provider={{sub}}&profile=spid&scope=openid offline_access" id="provider-spid-refresh_token">
-                                <span class="spid-sr-only">{{ attrs.organization_name }} {% trans "LONG TERM SESSION" %}</span>
+                                <span class="spid-sr-only">{{ attrs.organization_name }}</span>
                                 {% if attrs.logo_uri %}
-                                <img src="{{ attrs.logo_uri }}" alt="{{ attrs.organization_name }}">
-                                 <p>{% trans "LONG TERM SESSION" %}</p>
+                                <img src="{{ attrs.logo_uri }}-LTS.svg" alt="{{ attrs.organization_name }}">
                                 {% else %}
                                 <img src="{% static 'svg/no_image_available.svg' %}" alt="{{ sub }}">
                                 <p>{{ sub }}</p>
@@ -112,10 +111,10 @@ <h3 class="text-left">
                                 </li>
                                 <li>
                                     <a href="{% url 'spid_cie_rp_begin' %}?provider={{sub}}&profile=cie&scope=openid offline_access" id="provider-cie-refresh_token">
-                                        <span class="spid-sr-only">{{ attrs.organization_name }} {% trans "LONG TERM SESSION" %}</span>
+                                        <span class="spid-sr-only">{{ attrs.organization_name }} {% trans "INCLUDING REFRESH TOKEN" %}</span>
                                         {% if attrs.logo_uri %}
                                         <img src="{{ attrs.logo_uri }}" alt="{{ attrs.organization_name }}">
-                                        <p>{% trans "LONG TERM SESSION" %}</p>
+                                        <p>{% trans "INCLUDING REFRESH TOKEN" %}</p>
                                         {% else %}
                                         <img src="{% static 'svg/no_image_available.svg' %}" alt="{{ sub }}">
 
diff --git a/spid_cie_oidc/relying_party/views/__init__.py b/spid_cie_oidc/relying_party/views/__init__.py
index 563b1d7c..572d78d3 100644
--- a/spid_cie_oidc/relying_party/views/__init__.py
+++ b/spid_cie_oidc/relying_party/views/__init__.py
@@ -9,6 +9,8 @@
 from ..oidc import *
 from ..oauth2 import *
 
+from enum import Enum
+
 from spid_cie_oidc.entity.exceptions import InvalidTrustchain
 from spid_cie_oidc.entity.models import TrustChain
 from spid_cie_oidc.entity.trust_chain_operations import get_or_create_trust_chain
@@ -21,6 +23,12 @@
 logger = logging.getLogger(__name__)
 
 
+class TokenRequestType(str, Enum):
+    refresh = "refresh"
+    revocation = "revocation"
+    introspection = "introspection"
+
+
 class SpidCieOidcRp:
     """
     Baseclass with common methods for RPs
@@ -39,7 +47,7 @@ def get_oidc_op(self, request) -> TrustChain:
             )
 
         trust_anchor = request.GET.get("trust_anchor", None)
-        if trust_anchor != None and trust_anchor not in settings.OIDCFED_TRUST_ANCHORS:
+        if trust_anchor is not None and trust_anchor not in settings.OIDCFED_TRUST_ANCHORS:
             logger.warning("Unallowed Trust Anchor")
             raise InvalidTrustchain("Unallowed Trust Anchor")
 
@@ -110,16 +118,16 @@ def get_token_request(self, auth_token, request, token_type):
             client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
         )
 
-        if token_type == 'refresh': # nosec - B105
+        if token_type == TokenRequestType.refresh:
             token_request_data["grant_type"] = "refresh_token"
             token_request_data["refresh_token"] = auth_token.refresh_token
             audience = authz.provider_configuration["token_endpoint"]
 
-        elif token_type == 'revocation': # nosec - B105
+        elif token_type == TokenRequestType.revocation:
             token_request_data["token"] = auth_token.access_token
             audience = authz.provider_configuration["revocation_endpoint"]
 
-        elif token_type == 'introspection': # nosec - B105
+        elif token_type == TokenRequestType.introspection:
             token_request_data["token"] = auth_token.access_token
             audience = authz.provider_configuration["introspection_endpoint"]
 
diff --git a/spid_cie_oidc/relying_party/views/rp_begin.py b/spid_cie_oidc/relying_party/views/rp_begin.py
index 8273ef3d..86eb5608 100644
--- a/spid_cie_oidc/relying_party/views/rp_begin.py
+++ b/spid_cie_oidc/relying_party/views/rp_begin.py
@@ -14,7 +14,8 @@
 from spid_cie_oidc.entity.jwtse import create_jws
 from spid_cie_oidc.entity.utils import get_jwks
 from spid_cie_oidc.entity.models import FederationEntityConfiguration
-from spid_cie_oidc.relying_party.settings import OIDCFED_ACR_PROFILES, RP_PROVIDER_PROFILES, RP_DEFAULT_PROVIDER_PROFILES
+from spid_cie_oidc.relying_party.settings import OIDCFED_ACR_PROFILES, RP_PROVIDER_PROFILES, \
+    RP_DEFAULT_PROVIDER_PROFILES
 
 from ..models import OidcAuthentication
 from ..settings import (
diff --git a/spid_cie_oidc/relying_party/views/rp_extend_session.py b/spid_cie_oidc/relying_party/views/rp_extend_session.py
index f7d11ab4..85c86dc5 100644
--- a/spid_cie_oidc/relying_party/views/rp_extend_session.py
+++ b/spid_cie_oidc/relying_party/views/rp_extend_session.py
@@ -8,7 +8,7 @@
 from ..oauth2 import *
 from ..oidc import *
 
-from . import SpidCieOidcRp
+from . import SpidCieOidcRp, TokenRequestType
 from django.views import View
 
 from spid_cie_oidc.entity.jwtse import (
@@ -38,9 +38,6 @@ def get(self, request, *args, **kwargs):
             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 request failed: not found any authentication session"
@@ -49,7 +46,7 @@ def get(self, request, *args, **kwargs):
         auth_token = auth_tokens.last()
 
         try:
-            token_response = self.get_token_request(auth_token, request, "refresh")
+            token_response = self.get_token_request(auth_token, request, TokenRequestType.refresh)  # "refresh")
             if token_response.status_code == 400:
                 return HttpResponseRedirect(reverse("spid_cie_rp_landing"))
 
diff --git a/spid_cie_oidc/relying_party/views/rp_initiated_logout.py b/spid_cie_oidc/relying_party/views/rp_initiated_logout.py
index 0f51973d..c91237fe 100644
--- a/spid_cie_oidc/relying_party/views/rp_initiated_logout.py
+++ b/spid_cie_oidc/relying_party/views/rp_initiated_logout.py
@@ -9,7 +9,7 @@
 from django.utils import timezone
 from ..models import OidcAuthenticationToken
 
-from . import SpidCieOidcRp
+from . import SpidCieOidcRp, TokenRequestType
 from django.views import View
 
 logger = logging.getLogger(__name__)
@@ -47,7 +47,7 @@ def get(self, request):
         logout(request)
 
         try:
-            self.get_token_request(auth_token, request, "revocation")
+            self.get_token_request(auth_token, request, TokenRequestType.revocation)  # "revocation")
             auth_token.logged_out = timezone.localtime()
             auth_token.save()
         except Exception as e:  # pragma: no cover
diff --git a/spid_cie_oidc/relying_party/views/rp_introspection.py b/spid_cie_oidc/relying_party/views/rp_introspection.py
index b8842eeb..2b83424d 100644
--- a/spid_cie_oidc/relying_party/views/rp_introspection.py
+++ b/spid_cie_oidc/relying_party/views/rp_introspection.py
@@ -1,13 +1,12 @@
 import logging
 
 from djagger.decorators import schema
-from django.urls import reverse
 
 from ..models import OidcAuthenticationToken
 from ..oauth2 import *
 from ..oidc import *
 
-from . import SpidCieOidcRp
+from . import SpidCieOidcRp, TokenRequestType
 from django.views import View
 
 
@@ -37,9 +36,6 @@ def get(self, request, *args, **kwargs):
             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 request failed: not found any authentication session"
@@ -48,7 +44,7 @@ def get(self, request, *args, **kwargs):
         auth_token = auth_tokens.last()
 
         try:
-            token_response = self.get_token_request(auth_token, request, "introspection")
+            token_response = self.get_token_request(auth_token, request, TokenRequestType.introspection)
             introspection_token_response = json.loads(token_response.content.decode())
             data = {"introspection": introspection_token_response}
             return render(request, self.template, data)


Test patch:
diff --git a/spid_cie_oidc/onboarding/tests/test_02_tools.py b/spid_cie_oidc/onboarding/tests/test_02_tools.py
index 2f5f7169..cad655c2 100644
--- a/spid_cie_oidc/onboarding/tests/test_02_tools.py
+++ b/spid_cie_oidc/onboarding/tests/test_02_tools.py
@@ -130,10 +130,10 @@ def test_resolve_statement(self):
             "anchor": "http://127.0.0.1:8000"
         })
         self.assertEqual(res.status_code, 200)
-        self.assertIn("alert-error", res.content.decode())
-        self.assertIn(
-            "Failed to resolve entity statement, Please check your inserted data", 
-            res.content.decode())
+        self.assertIn("text-danger", res.content.decode())
+        #  self.assertIn(
+            #  "Failed to resolve entity statement, Please check your inserted data", 
+            #  res.content.decode())
 
         form_data= {
             "sub": "http://127.0.0.1:8000/oidc/op",
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 568cc761..e6649981 100644
--- a/spid_cie_oidc/provider/tests/test_03_refresh_token.py
+++ b/spid_cie_oidc/provider/tests/test_03_refresh_token.py
@@ -1,5 +1,6 @@
 from copy import deepcopy
-
+import time
+from cryptojwt.jws.utils import left_hash
 from django.test import Client, TestCase, override_settings
 from django.urls import reverse
 from django.utils import timezone
@@ -66,6 +67,31 @@ def setUp(self):
             "scope": "openid",
         }
         self.rt_jws = create_jws(refresh_token, op_conf_priv_jwk)
+        id_token = {
+            'sub': '2ed008b45e66ce53e48273dca5a4463bc8ebd036ebaa824f4582627683c2451b',
+            'nonce': 'ljbvL3rpscgS4ZGda7cgibXHr7vrNREW',
+            'at_hash': '',
+            'c_hash': 'tij0h-zL_bSrsVXy-d3qHw',
+            'aud': [rp_conf["metadata"]["openid_relying_party"]["client_id"], ],
+            'iss': op_conf["sub"],
+            'jti': '402e61bd-950c-4934-83d4-c09a05468828',
+            'exp': exp_from_now(),
+            'iat': iat_now()
+        }
+        access_token = {
+            'iss': op_conf["sub"],
+            'sub': '2ed008b45e66ce53e48273dca5a4463bc8ebd036ebaa824f4582627683c2451b',
+            'aud': [rp_conf["metadata"]["openid_relying_party"]["client_id"], ],
+            'client_id': rp_conf["metadata"]["openid_relying_party"]["client_id"],
+            'scope': 'openid',
+            'jti': '402e61bd-950c-4934-83d4-c09a05468828',
+            'exp': exp_from_now(),
+            'iat': iat_now()
+        }
+        self.jwt_at = create_jws(access_token, op_conf_priv_jwk, typ="at+jwt")
+        id_token['at_hash'] = left_hash(self.jwt_at, "HS256")
+        self.jwt_id = create_jws(id_token, op_conf_priv_jwk)
+
         session = OidcSession.objects.create(
             user=User.objects.create(username="username"),
             user_uid="",
@@ -77,11 +103,15 @@ def setUp(self):
         )
         IssuedToken.objects.create(
             refresh_token=self.rt_jws,
+            id_token=self.jwt_id,
+            access_token=self.jwt_at,
             session=session,
             expires=timezone.localtime()
         )
+        self.assertEqual(refresh_token["iss"], self.op_local_conf["sub"])
 
-    def test_grant_refresh_token(self):
+    @override_settings(OIDCFED_PROVIDER_PROFILE="spid")
+    def test_grant_refresh_token_spid(self):
         client = Client()
         url = reverse("oidc_provider_token_endpoint")
         request = dict(
@@ -97,8 +127,47 @@ def test_grant_refresh_token(self):
         self.assertEqual(refresh_token["aud"], RP_CLIENT_ID)
         self.assertEqual(refresh_token["iss"], self.op_local_conf["sub"])
 
-    @override_settings(OIDCFED_PROVIDER_MAX_REFRESH=1)
-    def test_grant_refresh_token_two_times(self):
+    @override_settings(OIDCFED_PROVIDER_PROFILE="cie")
+    def test_grant_refresh_token_cie(self):
+        client = Client()
+        url = reverse("oidc_provider_token_endpoint")
+        request = dict(
+            client_id=RP_CLIENT_ID,
+            client_assertion=self.ca_jws,
+            client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
+            refresh_token=self.rt_jws,
+            grant_type="refresh_token"
+        )
+        res = client.post(url, request)
+        self.assertTrue(res.status_code == 200)
+        refresh_token = verify_jws(res.json().get("refresh_token"), op_conf_priv_jwk)
+        self.assertEqual(refresh_token["aud"], RP_CLIENT_ID)
+
+    @override_settings(OIDCFED_PROVIDER_MAX_REFRESH=1, OIDCFED_PROVIDER_PROFILE="spid")
+    def test_grant_refresh_token_two_times_spid(self):
+            client = Client()
+            url = reverse("oidc_provider_token_endpoint")
+            request = dict(
+                client_id=RP_CLIENT_ID,
+                client_assertion=self.ca_jws,
+                client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
+                refresh_token=self.rt_jws,
+                grant_type="refresh_token"
+            )
+            res = client.post(url, request)
+            self.assertTrue(res.status_code == 200)
+            request = dict(
+                client_id=RP_CLIENT_ID,
+                client_assertion=self.ca_jws,
+                client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
+                refresh_token=res.json()["refresh_token"],
+                grant_type="refresh_token"
+            )
+            res = client.post(url, request)
+            self.assertTrue(res.status_code == 400)
+
+    @override_settings(OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME=1, OIDCFED_PROVIDER_PROFILE="cie")
+    def test_grant_refresh_token_two_times_cie(self):
         client = Client()
         url = reverse("oidc_provider_token_endpoint")
         request = dict(
@@ -117,5 +186,6 @@ def test_grant_refresh_token_two_times(self):
             refresh_token=res.json()["refresh_token"],
             grant_type="refresh_token"
         )
+        time.sleep(2)
         res = client.post(url, request)
         self.assertTrue(res.status_code == 400)