@@ -97,6 +97,26 @@ class OAuth2CodeGrantTests: XCTestCase {
9797 XCTAssertTrue ( 8 == ( query [ " state " ] !) . count, " Expecting an auto-generated UUID for `state` " )
9898 }
9999
100+ func testAuthorizeURIWithPKCE( ) {
101+ let oauth = OAuth2CodeGrant ( settings: [
102+ " client_id " : " abc " ,
103+ " authorize_uri " : " https://auth.ful.io " ,
104+ " token_uri " : " https://token.ful.io " ,
105+ " keychain " : false ,
106+ " use_pkce " : true ,
107+ ] )
108+ XCTAssertNotNil ( oauth. authURL, " Must init `authorize_uri` " )
109+
110+ let comp = URLComponents ( url: try ! oauth. authorizeURL ( withRedirect: " oauth2://callback " , scope: nil , params: nil ) , resolvingAgainstBaseURL: true ) !
111+ XCTAssertEqual ( comp. host!, " auth.ful.io " , " Correct host " )
112+ let query = OAuth2CodeGrant . params ( fromQuery: comp. percentEncodedQuery!)
113+ XCTAssertEqual ( query [ " client_id " ] !, " abc " , " Expecting correct `client_id` " )
114+ XCTAssertNotNil ( query [ " code_challenge " ] , " Must have `code_challenge` " )
115+ XCTAssertEqual ( query [ " code_challenge_method " ] !, " S256 " , " Expecting correct `code_challenge_method` " )
116+ XCTAssertEqual ( query [ " redirect_uri " ] !, " oauth2://callback " , " Expecting correct `redirect_uri` " )
117+ XCTAssertTrue ( 8 == ( query [ " state " ] !) . count, " Expecting an auto-generated UUID for `state` " )
118+ }
119+
100120 func testRedirectURI( ) {
101121 let oauth = OAuth2CodeGrant ( settings: baseSettings)
102122 oauth. redirect = " oauth2://callback "
@@ -253,7 +273,50 @@ class OAuth2CodeGrantTests: XCTestCase {
253273 XCTAssertEqual ( query2 [ " redirect_uri " ] !, " oauth2://callback " , " Expecting correct `redirect_uri` " )
254274 XCTAssertNil ( query2 [ " state " ] , " `state` must be empty " )
255275 }
256-
276+
277+ func testTokenRequestWithPKCE( ) {
278+ let oauth = OAuth2CodeGrant ( settings: [
279+ " client_id " : " abc " ,
280+ " authorize_uri " : " https://auth.ful.io " ,
281+ " token_uri " : " https://token.ful.io " ,
282+ " keychain " : false ,
283+ " use_pkce " : true ,
284+ ] )
285+ oauth. redirect = " oauth2://callback "
286+
287+ // no redirect in context - fail
288+ do {
289+ _ = try oauth. accessTokenRequest ( with: " pp " )
290+ XCTAssertTrue ( false , " Should not be here any more " )
291+ }
292+ catch OAuth2Error . noRedirectURL {
293+ XCTAssertTrue ( true , " Must be here " )
294+ }
295+ catch {
296+ XCTAssertTrue ( false , " Should not be here " )
297+ }
298+
299+ // with redirect in context - success
300+ oauth. context. redirectURL = " oauth2://callback "
301+
302+ // initialize code verifier in context
303+ oauth. context. generateCodeVerifier ( )
304+
305+ let req = try ! oauth. accessTokenRequest ( with: " pp " ) . asURLRequest ( for: oauth)
306+ let comp = URLComponents ( url: req. url!, resolvingAgainstBaseURL: true ) !
307+ XCTAssertEqual ( comp. host!, " token.ful.io " , " Correct host " )
308+
309+ let body = String ( data: req. httpBody!, encoding: String . Encoding. utf8)
310+ let query = OAuth2CodeGrant . params ( fromQuery: body!)
311+ XCTAssertEqual ( query [ " client_id " ] !, " abc " , " Expecting correct `client_id` " )
312+ XCTAssertNil ( query [ " client_secret " ] , " Must not have `client_secret` " )
313+ XCTAssertEqual ( query [ " code " ] !, " pp " , " Expecting correct `code` " )
314+ XCTAssertEqual ( query [ " grant_type " ] !, " authorization_code " , " Expecting correct `grant_type` " )
315+ XCTAssertEqual ( query [ " redirect_uri " ] !, " oauth2://callback " , " Expecting correct `redirect_uri` " )
316+ XCTAssertNil ( query [ " state " ] , " `state` must be empty " )
317+ XCTAssertNotNil ( query [ " code_verifier " ] , " Must have `code_verifier` " )
318+ }
319+
257320 func testCustomAuthParameters( ) {
258321 let oauth = OAuth2CodeGrant ( settings: baseSettings)
259322 oauth. redirect = " oauth2://callback "
0 commit comments