Skip to content

Commit da64c49

Browse files
ballandajandrade
authored andcommitted
client config: make state parameter optional
The state parameter is recommended but not mandatory so make this configurable in case the server does not support it. By default this is set to true in order to prevent cross-site request forgery. There is a pending PR in the main repo p2#284
1 parent 30f46ad commit da64c49

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

Sources/Base/OAuth2Base.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -420,12 +420,15 @@ open class OAuth2Base: OAuth2Securable {
420420
This method checks `state`, throws `OAuth2Error.missingState` or `OAuth2Error.invalidState`. Resets state if it matches.
421421
*/
422422
public final func assureMatchesState(_ params: OAuth2JSON) throws {
423-
guard let state = params["state"] as? String, !state.isEmpty else {
424-
throw OAuth2Error.missingState
425-
}
426-
logger?.trace("OAuth2", msg: "Checking state, got “\(state)”, expecting “\(context.state)")
427-
if !context.matchesState(state) {
428-
throw OAuth2Error.invalidState
423+
if let state = params["state"] as? String, !state.isEmpty {
424+
logger?.trace("OAuth2", msg: "Checking state, got “\(state)”, expecting “\(context.state)")
425+
if !context.matchesState(state) {
426+
throw OAuth2Error.invalidState
427+
}
428+
} else {
429+
if !clientConfig.stateParameterOptional {
430+
throw OAuth2Error.missingState
431+
}
429432
}
430433
context.resetState()
431434
}

Sources/Base/OAuth2ClientConfig.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ open class OAuth2ClientConfig {
7474

7575
/// Add custom parameters to the authorization request.
7676
public var customParameters: [String: String]? = nil
77+
78+
/// Whether the state parameter is optional
79+
public var stateParameterOptional = false
7780

7881
/// Most servers use UTF-8 encoding for Authorization headers, but that's not 100% true: make it configurable (see https://github.com/p2/OAuth2/issues/165).
7982
open var authStringEncoding = String.Encoding.utf8
@@ -146,7 +149,9 @@ open class OAuth2ClientConfig {
146149
if let params = settings["parameters"] as? OAuth2StringDict {
147150
customParameters = params
148151
}
149-
152+
if let stateOptional = settings["state_parameter_optional"] as? Bool {
153+
stateParameterOptional = stateOptional
154+
}
150155
// access token options
151156
if let assume = settings["token_assume_unexpired"] as? Bool {
152157
accessTokenAssumeUnexpired = assume

Tests/FlowTests/OAuth2CodeGrantTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,28 @@ class OAuth2CodeGrantTests: XCTestCase {
199199
XCTAssertTrue(false, "Should not throw, but threw \(error)")
200200
}
201201
}
202+
203+
func testRedirectURINoStateParameterAllowed() {
204+
let settings: OAuth2JSON = [
205+
"client_id": "abc",
206+
"client_secret": "xyz",
207+
"authorize_uri": "https://auth.ful.io",
208+
"token_uri": "https://token.ful.io",
209+
"keychain": false,
210+
"state_parameter_optional": true
211+
]
212+
let oauth = OAuth2CodeGrant(settings: settings)
213+
oauth.redirect = "oauth2://callback"
214+
oauth.context.redirectURL = oauth.redirect
215+
// parse no state
216+
let redirect = URL(string: "oauth2://callback?code=C0D3")!
217+
do {
218+
_ = try oauth.validateRedirectURL(redirect)
219+
}
220+
catch let error {
221+
XCTAssertTrue(false, "Must not end up here with \(error)")
222+
}
223+
}
202224

203225
func testTokenRequest() {
204226
let oauth = OAuth2CodeGrant(settings: [

0 commit comments

Comments
 (0)