|  | 
| 1 | 1 | import hashlib | 
| 2 | 2 | import json | 
| 3 | 3 | import logging | 
|  | 4 | +import secrets | 
| 4 | 5 | from urllib.parse import parse_qsl, urlencode, urlparse | 
| 5 | 6 | 
 | 
| 6 | 7 | from django.contrib.auth.mixins import LoginRequiredMixin | 
|  | 
| 21 | 22 | from ..scopes import get_scopes_backend | 
| 22 | 23 | from ..settings import oauth2_settings | 
| 23 | 24 | from ..signals import app_authorized | 
|  | 25 | +from ..utils import session_management_state_key | 
| 24 | 26 | from .mixins import OAuthLibMixin | 
| 25 | 27 | 
 | 
| 26 | 28 | 
 | 
| @@ -135,11 +137,43 @@ def form_valid(self, form): | 
| 135 | 137 | 
 | 
| 136 | 138 |         try: | 
| 137 | 139 |             uri, headers, body, status = self.create_authorization_response( | 
| 138 |  | -                request=self.request, scopes=scopes, credentials=credentials, allow=allow | 
|  | 140 | +                request=self.request, | 
|  | 141 | +                scopes=scopes, | 
|  | 142 | +                credentials=credentials, | 
|  | 143 | +                allow=allow, | 
| 139 | 144 |             ) | 
| 140 | 145 |         except OAuthToolkitError as error: | 
| 141 | 146 |             return self.error_response(error, application) | 
| 142 | 147 | 
 | 
|  | 148 | +        if oauth2_settings.OIDC_SESSION_MANAGEMENT_ENABLED: | 
|  | 149 | +            # https://openid.net/specs/openid-connect-session-1_0.html#CreatingUpdatingSessions | 
|  | 150 | + | 
|  | 151 | +            # When the OP supports session management, it MUST also | 
|  | 152 | +            # return the Session State as an additional session_state | 
|  | 153 | +            # parameter in the Authentication Response, the value is | 
|  | 154 | +            # based on a salted cryptographic hash of Client ID, | 
|  | 155 | +            # origin URL, and OP User Agent state. | 
|  | 156 | +            parsed = urlparse(uri) | 
|  | 157 | +            client_origin = f"{parsed.scheme}://{parsed.netloc}" | 
|  | 158 | + | 
|  | 159 | +            # Create random salt. | 
|  | 160 | +            salt = secrets.token_urlsafe(16) | 
|  | 161 | +            encoded = " ".join( | 
|  | 162 | +                [ | 
|  | 163 | +                    self.client.client_id, | 
|  | 164 | +                    client_origin, | 
|  | 165 | +                    session_management_state_key(self.request), | 
|  | 166 | +                    salt, | 
|  | 167 | +                ] | 
|  | 168 | +            ).encode("utf-8") | 
|  | 169 | +            hashed = hashlib.sha256(encoded) | 
|  | 170 | +            session_state = f"{hashed.hexdigest()}.{salt}" | 
|  | 171 | + | 
|  | 172 | +            # Add the session_state parameter to the query string | 
|  | 173 | +            qs = dict(parse_qsl(parsed.query)) | 
|  | 174 | +            qs["session_state"] = session_state | 
|  | 175 | +            uri = parsed._replace(query=urlencode(qs)).geturl() | 
|  | 176 | + | 
| 143 | 177 |         self.success_url = uri | 
| 144 | 178 |         log.debug("Success url for the request: {0}".format(self.success_url)) | 
| 145 | 179 |         return self.redirect(self.success_url, application) | 
| @@ -197,15 +231,20 @@ def get(self, request, *args, **kwargs): | 
| 197 | 231 |             # are already approved. | 
| 198 | 232 |             if application.skip_authorization: | 
| 199 | 233 |                 uri, headers, body, status = self.create_authorization_response( | 
| 200 |  | -                    request=self.request, scopes=" ".join(scopes), credentials=credentials, allow=True | 
|  | 234 | +                    request=self.request, | 
|  | 235 | +                    scopes=" ".join(scopes), | 
|  | 236 | +                    credentials=credentials, | 
|  | 237 | +                    allow=True, | 
| 201 | 238 |                 ) | 
| 202 | 239 |                 return self.redirect(uri, application) | 
| 203 | 240 | 
 | 
| 204 | 241 |             elif require_approval == "auto": | 
| 205 | 242 |                 tokens = ( | 
| 206 | 243 |                     get_access_token_model() | 
| 207 | 244 |                     .objects.filter( | 
| 208 |  | -                        user=request.user, application=kwargs["application"], expires__gt=timezone.now() | 
|  | 245 | +                        user=request.user, | 
|  | 246 | +                        application=kwargs["application"], | 
|  | 247 | +                        expires__gt=timezone.now(), | 
| 209 | 248 |                     ) | 
| 210 | 249 |                     .all() | 
| 211 | 250 |                 ) | 
|  | 
0 commit comments