Skip to content
This repository was archived by the owner on Feb 5, 2025. It is now read-only.

Commit d170cbd

Browse files
authored
Merge more standalone changes for the SDK-less Google-SignIn (#717)
2 parents 885edc5 + 8b6f6a8 commit d170cbd

13 files changed

+322
-10
lines changed

WordPressAuthenticator.xcodeproj/project.pbxproj

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,22 @@
2020
3F550D4E23DA429B007E5897 /* AppSelectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F550D4D23DA429B007E5897 /* AppSelectorTests.swift */; };
2121
3F550D5123DA4A9C007E5897 /* LinkMailPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F550D5023DA4A9C007E5897 /* LinkMailPresenter.swift */; };
2222
3F550D5323DA4AC6007E5897 /* URLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F550D5223DA4AC6007E5897 /* URLHandler.swift */; };
23+
3F879FD5293A3AB6005C2B48 /* OAuthTokenRequestBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F879FD4293A3AB6005C2B48 /* OAuthTokenRequestBody.swift */; };
24+
3F879FD7293A44F2005C2B48 /* OAuthTokenRequestBodyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F879FD6293A44F2005C2B48 /* OAuthTokenRequestBodyTests.swift */; };
25+
3F879FD9293A48B2005C2B48 /* OAuthTokenResponseBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F879FD8293A48B2005C2B48 /* OAuthTokenResponseBody.swift */; };
26+
3F879FDD293A500D005C2B48 /* URLRequest+OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F879FDC293A500D005C2B48 /* URLRequest+OAuth.swift */; };
27+
3F879FDF293A501D005C2B48 /* URLRequest+OAuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F879FDE293A501D005C2B48 /* URLRequest+OAuthTests.swift */; };
28+
3F879FE2293A53F5005C2B48 /* OAuthRequestBody+GoogleSignIn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F879FE0293A53CB005C2B48 /* OAuthRequestBody+GoogleSignIn.swift */; };
29+
3F879FE4293A545C005C2B48 /* OAuthRequestBody+GoogleSignInTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F879FE3293A545C005C2B48 /* OAuthRequestBody+GoogleSignInTests.swift */; };
2330
3F9439BE27D6F9B60067183A /* LoginPrologueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F9439BD27D6F9B60067183A /* LoginPrologueViewController.swift */; };
2431
3FE8071529364C410088420C /* Result+ConvenienceInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE8071429364C410088420C /* Result+ConvenienceInitTests.swift */; };
2532
3FE80717293650190088420C /* Result+ConvenienceInit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE80716293650190088420C /* Result+ConvenienceInit.swift */; };
2633
3FE8071B2936515F0088420C /* ASWebAuthenticationSession+Utils.swift .swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE8071A2936515F0088420C /* ASWebAuthenticationSession+Utils.swift .swift */; };
2734
3FE8071D293652BB0088420C /* OAuthError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE8071C293652BB0088420C /* OAuthError.swift */; };
2835
3FE8071F2936558F0088420C /* URL+GoogleSignInTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE8071E2936558F0088420C /* URL+GoogleSignInTests.swift */; };
2936
3FE8072129365F6D0088420C /* URL+GoogleSignIn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE8072029365F6D0088420C /* URL+GoogleSignIn.swift */; };
37+
3FEC44F7293A0E4600EBDECF /* ProofKeyForCodeExchange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FEC44F6293A0E4600EBDECF /* ProofKeyForCodeExchange.swift */; };
38+
3FEC44F9293A0F2900EBDECF /* ProofKeyForCodeExchangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FEC44F8293A0F2900EBDECF /* ProofKeyForCodeExchangeTests.swift */; };
3039
3FFF2FC123D7ED7C00D38C77 /* EmailClients.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3FFF2FC023D7ED7C00D38C77 /* EmailClients.plist */; };
3140
3FFF2FC323D7F53200D38C77 /* AppSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFF2FC223D7F53200D38C77 /* AppSelector.swift */; };
3241
4A1DEF4A29341B1F00322608 /* LoggingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1DEF4829341B1F00322608 /* LoggingTests.m */; };
@@ -236,13 +245,22 @@
236245
3F550D4D23DA429B007E5897 /* AppSelectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSelectorTests.swift; sourceTree = "<group>"; };
237246
3F550D5023DA4A9C007E5897 /* LinkMailPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkMailPresenter.swift; sourceTree = "<group>"; };
238247
3F550D5223DA4AC6007E5897 /* URLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLHandler.swift; sourceTree = "<group>"; };
248+
3F879FD4293A3AB6005C2B48 /* OAuthTokenRequestBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthTokenRequestBody.swift; sourceTree = "<group>"; };
249+
3F879FD6293A44F2005C2B48 /* OAuthTokenRequestBodyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthTokenRequestBodyTests.swift; sourceTree = "<group>"; };
250+
3F879FD8293A48B2005C2B48 /* OAuthTokenResponseBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthTokenResponseBody.swift; sourceTree = "<group>"; };
251+
3F879FDC293A500D005C2B48 /* URLRequest+OAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+OAuth.swift"; sourceTree = "<group>"; };
252+
3F879FDE293A501D005C2B48 /* URLRequest+OAuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+OAuthTests.swift"; sourceTree = "<group>"; };
253+
3F879FE0293A53CB005C2B48 /* OAuthRequestBody+GoogleSignIn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OAuthRequestBody+GoogleSignIn.swift"; sourceTree = "<group>"; };
254+
3F879FE3293A545C005C2B48 /* OAuthRequestBody+GoogleSignInTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OAuthRequestBody+GoogleSignInTests.swift"; sourceTree = "<group>"; };
239255
3F9439BD27D6F9B60067183A /* LoginPrologueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginPrologueViewController.swift; sourceTree = "<group>"; };
240256
3FE8071429364C410088420C /* Result+ConvenienceInitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+ConvenienceInitTests.swift"; sourceTree = "<group>"; };
241257
3FE80716293650190088420C /* Result+ConvenienceInit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+ConvenienceInit.swift"; sourceTree = "<group>"; };
242258
3FE8071A2936515F0088420C /* ASWebAuthenticationSession+Utils.swift .swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ASWebAuthenticationSession+Utils.swift .swift"; sourceTree = "<group>"; };
243259
3FE8071C293652BB0088420C /* OAuthError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthError.swift; sourceTree = "<group>"; };
244260
3FE8071E2936558F0088420C /* URL+GoogleSignInTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+GoogleSignInTests.swift"; sourceTree = "<group>"; };
245261
3FE8072029365F6D0088420C /* URL+GoogleSignIn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+GoogleSignIn.swift"; sourceTree = "<group>"; };
262+
3FEC44F6293A0E4600EBDECF /* ProofKeyForCodeExchange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProofKeyForCodeExchange.swift; sourceTree = "<group>"; };
263+
3FEC44F8293A0F2900EBDECF /* ProofKeyForCodeExchangeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProofKeyForCodeExchangeTests.swift; sourceTree = "<group>"; };
246264
3FFF2FC023D7ED7C00D38C77 /* EmailClients.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = EmailClients.plist; sourceTree = "<group>"; };
247265
3FFF2FC223D7F53200D38C77 /* AppSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSelector.swift; sourceTree = "<group>"; };
248266
4A1DEF4829341B1F00322608 /* LoggingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoggingTests.m; sourceTree = "<group>"; };
@@ -480,22 +498,30 @@
480498
children = (
481499
3FE8071A2936515F0088420C /* ASWebAuthenticationSession+Utils.swift .swift */,
482500
3FE8071C293652BB0088420C /* OAuthError.swift */,
501+
3F879FD4293A3AB6005C2B48 /* OAuthTokenRequestBody.swift */,
502+
3F879FD8293A48B2005C2B48 /* OAuthTokenResponseBody.swift */,
503+
3FEC44F6293A0E4600EBDECF /* ProofKeyForCodeExchange.swift */,
483504
3FE80716293650190088420C /* Result+ConvenienceInit.swift */,
505+
3F879FDC293A500D005C2B48 /* URLRequest+OAuth.swift */,
484506
);
485507
path = OAuth;
486508
sourceTree = "<group>";
487509
};
488510
3FE807192936504F0088420C /* OAuth */ = {
489511
isa = PBXGroup;
490512
children = (
513+
3F879FD6293A44F2005C2B48 /* OAuthTokenRequestBodyTests.swift */,
514+
3FEC44F8293A0F2900EBDECF /* ProofKeyForCodeExchangeTests.swift */,
491515
3FE8071429364C410088420C /* Result+ConvenienceInitTests.swift */,
516+
3F879FDE293A501D005C2B48 /* URLRequest+OAuthTests.swift */,
492517
);
493518
path = OAuth;
494519
sourceTree = "<group>";
495520
};
496521
3FE8072229365F740088420C /* GoogleSignIn */ = {
497522
isa = PBXGroup;
498523
children = (
524+
3F879FE0293A53CB005C2B48 /* OAuthRequestBody+GoogleSignIn.swift */,
499525
3FE8072029365F6D0088420C /* URL+GoogleSignIn.swift */,
500526
);
501527
path = GoogleSignIn;
@@ -504,6 +530,7 @@
504530
3FE8072329365FC20088420C /* GoogleSignIn */ = {
505531
isa = PBXGroup;
506532
children = (
533+
3F879FE3293A545C005C2B48 /* OAuthRequestBody+GoogleSignInTests.swift */,
507534
3FE8071E2936558F0088420C /* URL+GoogleSignInTests.swift */,
508535
);
509536
path = GoogleSignIn;
@@ -1295,6 +1322,7 @@
12951322
files = (
12961323
CE73475624B77A3800A22660 /* SiteCredentialsViewController.swift in Sources */,
12971324
EE633D02287560E50002DE03 /* UITableView+Helpers.swift in Sources */,
1325+
3F879FD5293A3AB6005C2B48 /* OAuthTokenRequestBody.swift in Sources */,
12981326
982C8E7923021C20003F1BA0 /* LoginPrologueLoginMethodViewController.swift in Sources */,
12991327
B5609144208A563800399AE4 /* LoginPrologueSignupMethodViewController.swift in Sources */,
13001328
B56090D1208A4F5400399AE4 /* NUXViewController.swift in Sources */,
@@ -1304,6 +1332,7 @@
13041332
CE1B18C920EEC2C200BECC3F /* SocialService.swift in Sources */,
13051333
F12F9FB424D8A68E00771BCE /* AuthenticatorAnalyticsTracker.swift in Sources */,
13061334
988AD8A324CB839900BD045E /* TwoFAViewController.swift in Sources */,
1335+
3F879FD9293A48B2005C2B48 /* OAuthTokenResponseBody.swift in Sources */,
13071336
CE6BCD2E24A3A235001BCDC5 /* TextLabelTableViewCell.swift in Sources */,
13081337
B56090D3208A4F5400399AE4 /* NUXLinkAuthViewController.swift in Sources */,
13091338
B5609120208A555E00399AE4 /* SignupNavigationController.swift in Sources */,
@@ -1313,6 +1342,7 @@
13131342
02A526CF28A3A35D00FD1812 /* PasswordCoordinator.swift in Sources */,
13141343
98ED483624802F8F00992B2D /* GoogleAuthViewController.swift in Sources */,
13151344
F5C817E72582B2F300BD5A3B /* UIPasteboard+Detect.swift in Sources */,
1345+
3FEC44F7293A0E4600EBDECF /* ProofKeyForCodeExchange.swift in Sources */,
13161346
B56090EA208A51D000399AE4 /* LoginFields+Validation.swift in Sources */,
13171347
F1DE08CC24F4266A007AE6B3 /* StoredCredentialsAuthenticator.swift in Sources */,
13181348
CE1B18CC20EEC32400BECC3F /* WordPressComCredentials.swift in Sources */,
@@ -1335,6 +1365,7 @@
13351365
3FE8071D293652BB0088420C /* OAuthError.swift in Sources */,
13361366
B5609119208A555600399AE4 /* SiteInfoHeaderView.swift in Sources */,
13371367
B560913E208A563800399AE4 /* SigninEditingState.swift in Sources */,
1368+
3F879FDD293A500D005C2B48 /* URLRequest+OAuth.swift in Sources */,
13381369
CE2D03E024E5DD4500D18942 /* UnifiedSignupViewController.swift in Sources */,
13391370
98CF18F7248725370047B66C /* GoogleSignupConfirmationViewController.swift in Sources */,
13401371
1A21EE9822832BC300C940C6 /* WordPressComOAuthClientFacade+Swift.swift in Sources */,
@@ -1355,6 +1386,7 @@
13551386
B56090F9208A533200399AE4 /* WordPressAuthenticator+Events.swift in Sources */,
13561387
CEDE0D93242011E000CB3345 /* NSObject+Helpers.swift in Sources */,
13571388
020DEF6428AA091100C85D51 /* MagicLinkRequester.swift in Sources */,
1389+
3F879FE2293A53F5005C2B48 /* OAuthRequestBody+GoogleSignIn.swift in Sources */,
13581390
020BE74A23B0BD2E007FE54C /* WordPressAuthenticatorDisplayImages.swift in Sources */,
13591391
B560913A208A563800399AE4 /* LoginLinkRequestViewController.swift in Sources */,
13601392
B560910C208A54F800399AE4 /* WordPressComOAuthClientFacade.m in Sources */,
@@ -1423,21 +1455,25 @@
14231455
B501C045208FC68700D1E58F /* LoginFieldsValidationTests.swift in Sources */,
14241456
BA53D64824DFDF97001F1ABF /* WordPressSourceTagTests.swift in Sources */,
14251457
4A1DEF4B29341B1F00322608 /* LoggingTests.swift in Sources */,
1458+
3F879FDF293A501D005C2B48 /* URLRequest+OAuthTests.swift in Sources */,
14261459
D8610CEC2570A60C00A5DF27 /* NavigationToRootTests.swift in Sources */,
14271460
BA53D64D24DFE4E6001F1ABF /* ModalViewControllerPresentingSpy.swift in Sources */,
14281461
BA53D64624DFDE1D001F1ABF /* CredentialsTests.swift in Sources */,
14291462
D85C36EC256E10EA00D56E34 /* MockNavigationController.swift in Sources */,
14301463
3F550D4E23DA429B007E5897 /* AppSelectorTests.swift in Sources */,
14311464
BA53D64B24DFE07D001F1ABF /* WordpressAuthenticatorProvider.swift in Sources */,
14321465
4A1DEF4A29341B1F00322608 /* LoggingTests.m in Sources */,
1466+
3FEC44F9293A0F2900EBDECF /* ProofKeyForCodeExchangeTests.swift in Sources */,
14331467
CE16177821B70C1A00B82A47 /* WordPressAuthenticatorDisplayTextTests.swift in Sources */,
14341468
B501C048208FC79C00D1E58F /* LoginFacadeTests.m in Sources */,
1469+
3F879FD7293A44F2005C2B48 /* OAuthTokenRequestBodyTests.swift in Sources */,
14351470
3108613125AFA4830022F75E /* PasteboardTests.swift in Sources */,
14361471
3FE8071F2936558F0088420C /* URL+GoogleSignInTests.swift in Sources */,
14371472
D85C36F0256E118D00D56E34 /* NavigationToEnterAccountTests.swift in Sources */,
14381473
D85C36E6256E0DDE00D56E34 /* NavigationToEnterSiteTests.swift in Sources */,
14391474
D85C3882256E3FEC00D56E34 /* WordPressComSiteInfoTests.swift in Sources */,
14401475
D8611A672576236800A5DF27 /* NavigateBackTests.swift in Sources */,
1476+
3F879FE4293A545C005C2B48 /* OAuthRequestBody+GoogleSignInTests.swift in Sources */,
14411477
F12F9FB824D8A7FC00771BCE /* AnalyticsTrackerTests.swift in Sources */,
14421478
B501C046208FC6A700D1E58F /* WordPressAuthenticatorTests.swift in Sources */,
14431479
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
extension OAuthTokenRequestBody {
2+
3+
static func googleSignInRequestBody(
4+
clientId: String,
5+
authCode: String,
6+
pkce: ProofKeyForCodeExchange
7+
) -> Self {
8+
.init(
9+
clientId: clientId,
10+
// "The client secret obtained from the API Console Credentials page."
11+
// - https://developers.google.com/identity/protocols/oauth2/native-app#step-2:-send-a-request-to-googles-oauth-2.0-server
12+
//
13+
// There doesn't seem to be any secret for iOS app credentials.
14+
// The process works with an empty string...
15+
clientSecret: "",
16+
code: authCode,
17+
codeVerifier: pkce.codeVerifier,
18+
// As defined in the OAuth 2.0 specification, this field's value must be set to authorization_code.
19+
// – https://developers.google.com/identity/protocols/oauth2/native-app#exchange-authorization-code
20+
grantType: "authorization_code",
21+
redirectURI: URL.redirectURI(from: clientId)
22+
)
23+
}
24+
}

WordPressAuthenticator/GoogleSignIn/URL+GoogleSignIn.swift

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,46 @@ import Foundation
22

33
extension URL {
44

5-
// TODO: This is incomplete
6-
static func googleSignInAuthURL(clientId: String) throws -> URL {
7-
let baseURL = "https://accounts.google.com/o/oauth2/v2/auth"
5+
// It's acceptable to force-unwrap here because, for this call to fail we'd need a developer
6+
// error, which we would catch because the unit tests would crash.
7+
static var googleSignInBaseURL = URL(string: "https://accounts.google.com/o/oauth2/v2/auth")!
88

9+
static func googleSignInAuthURL(clientId: String, pkce: ProofKeyForCodeExchange) throws -> URL {
910
let queryItems = [
1011
("client_id", clientId),
12+
("code_challenge", pkce.codeCallenge),
13+
("code_challenge_method", pkce.method.urlQueryParameterValue),
1114
("redirect_uri", redirectURI(from: clientId)),
12-
("response_type", "code")
15+
("response_type", "code"),
16+
// TODO: We might want to add some of these or them configurable
17+
//
18+
// The request we make with the SDK asks for:
19+
//
20+
// - email
21+
// - profile
22+
// - https://www.googleapis.com/auth/userinfo.email
23+
// - https://www.googleapis.com/auth/userinfo.profile
24+
// - openid
25+
//
26+
// See https://developers.google.com/identity/protocols/oauth2/scopes
27+
("scope", "https://www.googleapis.com/auth/userinfo.email")
1328
].map { URLQueryItem(name: $0.0, value: $0.1) }
1429

1530
if #available(iOS 16.0, *) {
16-
return URL(string: baseURL)!.appending(queryItems: queryItems)
31+
return googleSignInBaseURL.appending(queryItems: queryItems)
1732
} else {
18-
var components = URLComponents(string: baseURL)!
33+
// Given `googleSignInBaseURL` is assumed as a valid URL, a `URLComponents` instance
34+
// should always be available.
35+
var components = URLComponents(url: googleSignInBaseURL, resolvingAgainstBaseURL: false)!
1936
components.queryItems = queryItems
20-
return try components.asURL()
37+
// Likewise, we can as long as the given `queryItems` are valid, we can assume `url` to
38+
// not be nil. If `queryItems` are invalid, a developer error has been committed, and
39+
// crashing is appropriate.
40+
return components.url!
2141
}
2242
}
2343

24-
private static func redirectURI(from clientId: String) -> String {
44+
static func redirectURI(from clientId: String) -> String {
2545
// Google's client id is in the form: 123-abc245def.apps.googleusercontent.com
2646
// The redirect URI uses the reverse-DNS notation.
2747
let reverseDNSClientId = clientId.split(separator: ".").reversed().joined(separator: ".")

WordPressAuthenticator/OAuth/OAuthError.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
enum OAuthError: LocalizedError {
22

3+
// ASWebAuthenticationSession
34
case inconsistentWebAuthenticationSessionCompletion
45

56
var errorDescription: String {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/// Models the request to send for an OAuth token
2+
///
3+
/// - Note: See documentation at https://developers.google.com/identity/protocols/oauth2/native-app#exchange-authorization-code
4+
struct OAuthTokenRequestBody: Encodable {
5+
let clientId: String
6+
let clientSecret: String
7+
let code: String
8+
let codeVerifier: String
9+
let grantType: String
10+
let redirectURI: String
11+
12+
enum CodingKeys: String, CodingKey {
13+
case clientId = "client_id"
14+
case clientSecret = "client_secret"
15+
case code
16+
case codeVerifier = "code_verifier"
17+
case grantType = "grant_type"
18+
case redirectURI = "redirect_uri"
19+
}
20+
21+
func asURLEncodedData() throws -> Data {
22+
let params = [
23+
(CodingKeys.clientId.rawValue, clientId),
24+
(CodingKeys.clientSecret.rawValue, clientSecret),
25+
(CodingKeys.code.rawValue, code),
26+
(CodingKeys.codeVerifier.rawValue, codeVerifier),
27+
(CodingKeys.grantType.rawValue, grantType),
28+
(CodingKeys.redirectURI.rawValue, redirectURI),
29+
]
30+
31+
let items = params.map { URLQueryItem(name: $0.0, value: $0.1) }
32+
33+
var components = URLComponents()
34+
components.queryItems = items
35+
36+
// We can assume `query` to never be nil because we set `queryItems` in the line above.
37+
return Data(components.query!.utf8)
38+
}
39+
}

0 commit comments

Comments
 (0)