Skip to content

Commit 5c07ab3

Browse files
author
Stéphane Senart
committed
Merge branch 'develop'
2 parents cbaadee + 445e476 commit 5c07ab3

3 files changed

Lines changed: 67 additions & 35 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.3.1] - 2025-07-22
8+
9+
### Fixed
10+
11+
[#88](https://github.com/ssenart/PyGazpar/issues/88) : 500 - NGINX / OpenID Connect login failure.
12+
713
## [1.3.0] - 2025-02-15
814

915
### Added

pygazpar/api_client.py

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import http.cookiejar
2-
import json
31
import logging
2+
import re
43
import time
54
import traceback
65
from datetime import date
@@ -9,21 +8,20 @@
98

109
from requests import Response, Session
1110

12-
SESSION_TOKEN_URL = "https://connexion.grdf.fr/api/v1/authn"
13-
SESSION_TOKEN_PAYLOAD = """{{
14-
"username": "{0}",
15-
"password": "{1}",
16-
"options": {{
17-
"multiOptionalFactorEnroll": "false",
18-
"warnBeforePasswordExpired": "false"
19-
}}
11+
START_URL = "https://monespace.grdf.fr/"
12+
13+
MAIL_SESSION_TOKEN_URL = "https://connexion.grdf.fr/idp/idx/identify"
14+
MAIL_SESSION_TOKEN_PAYLOAD = """{{
15+
"identifier": "{0}",
16+
"stateHandle": "{1}"
2017
}}"""
2118

22-
AUTH_TOKEN_URL = "https://connexion.grdf.fr/login/sessionCookieRedirect"
23-
AUTH_TOKEN_PARAMS = """{{
24-
"checkAccountSetupComplete": "true",
25-
"token": "{0}",
26-
"redirectUrl": "https://monespace.grdf.fr"
19+
PASSWORD_SESSION_TOKEN_URL = "https://connexion.grdf.fr/idp/idx/challenge/answer"
20+
PASSWORD_SESSION_TOKEN_PAYLOAD = """{{
21+
"credentials": {{
22+
"passcode": "{0}"
23+
}},
24+
"stateHandle": "{1}"
2725
}}"""
2826

2927
API_BASE_URL = "https://monespace.grdf.fr/api"
@@ -71,46 +69,74 @@ def __init__(self, username: str, password: str, retry_count: int = 10):
7169
self._username = username
7270
self._password = password
7371
self._retry_count = retry_count
74-
self._session = None
72+
self._session: Session | None = None
7573

7674
# ------------------------------------------------------
7775
def login(self):
7876
if self._session is not None:
7977
return
8078

8179
session = Session()
82-
session.headers.update({"domain": "grdf.fr"})
83-
session.headers.update({"Content-Type": "application/json"})
84-
session.headers.update({"X-Requested-With": "XMLHttpRequest"})
80+
session.headers.update({"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"})
8581

86-
payload = SESSION_TOKEN_PAYLOAD.format(self._username, self._password)
82+
start_response = session.get(START_URL)
83+
if start_response.status_code != 200:
84+
raise ServerError(
85+
f"An error occurred while logging in start. Status code: {start_response.status_code} - {start_response.url}",
86+
start_response.status_code,
87+
)
8788

88-
response = session.post(SESSION_TOKEN_URL, data=payload)
89+
pattern = r'"stateToken"\s*:\s*"([^"]+)"'
90+
match = re.search(pattern, start_response.text)
91+
if match:
92+
state_token_html = match.group(1)
93+
state_token = state_token_html.replace("\\x2D", "-")
94+
else:
95+
raise ValueError("Cannot retrieve stateToken inside HTML response")
96+
97+
payload = MAIL_SESSION_TOKEN_PAYLOAD.format(self._username, state_token)
98+
session.cookies.set("ln", self._username)
99+
100+
mail_response = session.post(
101+
MAIL_SESSION_TOKEN_URL,
102+
data=payload,
103+
headers={"Accept": "application/json; okta-version=1.0.0", "Content-Type": "application/json"},
104+
)
89105

90-
if response.status_code != 200:
106+
if mail_response.status_code != 200:
91107
raise ServerError(
92-
f"An error occurred while logging in. Status code: {response.status_code} - {response.text}",
93-
response.status_code,
108+
f"An error occurred while logging in mail. Status code: {mail_response.status_code} - {mail_response.text}",
109+
mail_response.status_code,
94110
)
95111

96-
session_token = response.json().get("sessionToken")
112+
state_handle = mail_response.json().get("stateHandle")
97113

98-
jar = http.cookiejar.CookieJar()
114+
payload = PASSWORD_SESSION_TOKEN_PAYLOAD.format(self._password, state_handle)
99115

100-
self._session = Session() # pylint: disable=attribute-defined-outside-init
101-
self._session.headers.update({"Content-Type": "application/json"})
102-
self._session.headers.update({"X-Requested-With": "XMLHttpRequest"})
116+
password_response = session.post(
117+
PASSWORD_SESSION_TOKEN_URL,
118+
data=payload,
119+
headers={"Accept": "application/json; okta-version=1.0.0", "Content-Type": "application/json"},
120+
)
103121

104-
params = json.loads(AUTH_TOKEN_PARAMS.format(session_token))
122+
if password_response.status_code != 200:
123+
raise ServerError(
124+
f"An error occurred while logging in password. Status code: {password_response.status_code} - {password_response.text}",
125+
password_response.status_code,
126+
)
105127

106-
response = self._session.get(AUTH_TOKEN_URL, params=params, allow_redirects=True, cookies=jar) # type: ignore
128+
success_url = password_response.json()["success"]["href"]
107129

108-
if response.status_code != 200:
130+
response_redirect = session.get(success_url)
131+
132+
if response_redirect.status_code != 200:
109133
raise ServerError(
110-
f"An error occurred while getting the auth token. Status code: {response.status_code} - {response.text}",
111-
response.status_code,
134+
f"An error occurred while logging in response_redirect. Status code: {response_redirect.status_code} - {response_redirect.url}",
135+
response_redirect.status_code,
112136
)
113137

138+
self._session = session
139+
114140
# ------------------------------------------------------
115141
def is_logged_in(self) -> bool:
116142
return self._session is not None

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "pygazpar"
3-
version = "1.3.0"
3+
version = "1.3.1"
44
description = "Python library to download gas consumption from a GrDF (French Gas Company) account"
55
license = { file = "LICENSE" }
66
readme = "README.md"

0 commit comments

Comments
 (0)