|
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