Skip to content

Commit ce1d8da

Browse files
authored
Update diagnostic errors for missing state in logout responses (#977)
1 parent a972daa commit ce1d8da

2 files changed

Lines changed: 38 additions & 12 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# UNRELEASED
2+
- Improved the state validation error messages to distinguish a missing `state` parameter from a mismatched one. The end session message now notes that a provider's `end_session_endpoint` may point at a legacy or SAML single-logout endpoint, which does not echo `state`. Addresses issue #956.
3+
14
# 3.0.0
25
- BREAKING: Updates made to support Xcode 27. ([#972](https://github.com/openid/AppAuth-iOS/pull/972), [#973](https://github.com/openid/AppAuth-iOS/pull/973))
36
-- Raised minimum deployment targets to iOS 15.0, macOS 12.0, tvOS 15.0 and watchOS 9.0 (minimum for Xcode 27).

‎Sources/AppAuthCore/OIDAuthorizationService.m‎

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,22 @@ - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL error:(NSError *_Nullabl
170170
// verifies that the state in the response matches the state in the request, or both are nil
171171
if (!OIDIsEqualIncludingNil(_request.state, response.state)) {
172172
NSMutableDictionary *userInfo = [query.dictionaryValue mutableCopy];
173-
userInfo[NSLocalizedDescriptionKey] =
174-
[NSString stringWithFormat:@"State mismatch, expecting %@ but got %@ in authorization "
175-
"response %@",
176-
_request.state,
177-
response.state,
178-
response];
173+
if (response.state == nil) {
174+
userInfo[NSLocalizedDescriptionKey] =
175+
[NSString stringWithFormat:@"The authorization response is missing the state parameter, "
176+
"expecting %@. RFC 6749 section 4.1.2 and OpenID Connect "
177+
"Core section 3.1.2.5 require the authorization server to "
178+
"echo the exact state value from the request. Response: %@",
179+
_request.state,
180+
response];
181+
} else {
182+
userInfo[NSLocalizedDescriptionKey] =
183+
[NSString stringWithFormat:@"State mismatch, expecting %@ but got %@ in authorization "
184+
"response %@",
185+
_request.state,
186+
response.state,
187+
response];
188+
}
179189
response = nil;
180190
responseError = [NSError errorWithDomain:OIDOAuthAuthorizationErrorDomain
181191
code:OIDErrorCodeOAuthAuthorizationClientError
@@ -307,12 +317,25 @@ - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL error:(NSError *_Nullabl
307317
// verifies that the state in the response matches the state in the request, or both are nil
308318
if (!OIDIsEqualIncludingNil(_request.state, response.state)) {
309319
NSMutableDictionary *userInfo = [query.dictionaryValue mutableCopy];
310-
userInfo[NSLocalizedDescriptionKey] =
311-
[NSString stringWithFormat:@"State mismatch, expecting %@ but got %@ in authorization "
312-
"response %@",
313-
_request.state,
314-
response.state,
315-
response];
320+
if (response.state == nil) {
321+
userInfo[NSLocalizedDescriptionKey] =
322+
[NSString stringWithFormat:@"The end session response is missing the state parameter, "
323+
"expecting %@. This may mean end_session_endpoint is not "
324+
"an OpenID Connect RP-Initiated Logout endpoint; some "
325+
"providers advertise a legacy or SAML single-logout "
326+
"endpoint under that key, and those do not echo state. "
327+
"Check end_session_endpoint in the provider's discovery "
328+
"document. Response: %@",
329+
_request.state,
330+
response];
331+
} else {
332+
userInfo[NSLocalizedDescriptionKey] =
333+
[NSString stringWithFormat:@"State in the end session response does not match the "
334+
"request, expecting %@ but got %@. Response: %@",
335+
_request.state,
336+
response.state,
337+
response];
338+
}
316339
response = nil;
317340
responseError = [NSError errorWithDomain:OIDOAuthAuthorizationErrorDomain
318341
code:OIDErrorCodeOAuthAuthorizationClientError

0 commit comments

Comments
 (0)