Skip to content

Commit a6cf24a

Browse files
author
Achyut Kumar M
committed
add OAuthViewController
1 parent 49d0f79 commit a6cf24a

File tree

2 files changed

+187
-2
lines changed

2 files changed

+187
-2
lines changed

GoInfoGame/GoInfoGame/UI/Login/OAuth/OAuthViewController.swift

Lines changed: 185 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,188 @@
55
// Created by Achyut Kumar M on 21/03/24.
66
//
77

8-
import Foundation
8+
import SwiftUI
9+
import SafariServices
10+
import osmapi
11+
12+
struct OAuthViewController: UIViewControllerRepresentable {
13+
14+
private struct OAuthServer {
15+
let authURL: String
16+
let apiURL: String
17+
let client_id: String
18+
}
19+
20+
private let servers: [OAuthServer] = [
21+
OAuthServer(authURL: "https://www.openstreetmap.org/",
22+
apiURL: "https://api.openstreetmap.org/",
23+
client_id: "oR9y-ytJ1O1OnM1hnPXc8WHjBwmephYdu3Az0a4rXNU"),
24+
25+
OAuthServer(authURL: "https://master.apis.dev.openstreetmap.org/",
26+
apiURL: "https://api06.dev.openstreetmap.org/",
27+
client_id: "oR9y-ytJ1O1OnM1hnPXc8WHjBwmephYdu3Az0a4rXNU")
28+
]
29+
30+
private let client_secret = "eRY8q6sI5JDA3FU2iw5awIyXShuGUD6z2hL0YTNjfGg"
31+
private var server: OAuthServer { servers.first(where: { $0.apiURL == OSM_API_URL }) ?? servers[1] }
32+
var client_id: String { server.client_id }
33+
var serverURL: String { server.authURL }
34+
var oauthUrl: URL { return URL(string: serverURL)!.appendingPathComponent("oauth2") }
35+
36+
private let redirect_uri = "goinfogame://oauth/callback"
37+
private let scope = "read_prefs write_prefs write_diary write_api write_notes write_redactions openid"
38+
39+
let state = "random"
40+
41+
func makeUIViewController(context: Context) -> SFSafariViewController {
42+
let url = url(withPath: "authorize", with: [
43+
"client_id": client_id,
44+
"redirect_uri": redirect_uri,
45+
"response_type": "code",
46+
"scope": scope,
47+
"state": state
48+
])
49+
return SFSafariViewController(url: url)
50+
}
51+
52+
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
53+
54+
}
55+
56+
func getAccessTokenFor(url: URL, completion: @escaping (String)-> Void) {
57+
58+
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
59+
// apiCallback(.failure(OAuthError.badRedirectURL(url.absoluteString)));
60+
//TODO: Handle error
61+
return
62+
}
63+
guard
64+
let code = components.queryItems?.first(where: { $0.name == "code" })?.value,
65+
let state = components.queryItems?.first(where: { $0.name == "state" })?.value
66+
else {
67+
// handleError(from: components)
68+
return
69+
}
70+
guard state == self.state else {
71+
// apiCallback(.failure(OAuthError.stateMismatch))
72+
//TODO: Handle error
73+
return
74+
}
75+
fetchAccessTokenFor(authCode: code, completion: completion)
76+
}
77+
78+
func fetchAccessTokenFor(authCode:String, completion: @escaping (String)-> Void) {
79+
let postingJSON = [
80+
"client_id": client_id,
81+
"redirect_uri": redirect_uri,
82+
"code": "\(authCode)",
83+
"grant_type": "authorization_code",
84+
"client_secret": client_secret,
85+
]
86+
87+
let postingBody = query(postingJSON).data(using: .utf8, allowLossyConversion: false)
88+
89+
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
90+
let url: String = "https://master.apis.dev.openstreetmap.org/oauth2/token"
91+
let request: NSMutableURLRequest = NSMutableURLRequest()
92+
request.url = NSURL(string: url) as URL?
93+
request.httpMethod = "POST"
94+
//add params to request
95+
request.httpBody = postingBody
96+
let dataTask = session.dataTask(with: request as URLRequest) { (data: Data?, response:URLResponse?, error: Error?) -> Void in
97+
if((error) != nil) {
98+
print(error!.localizedDescription)
99+
} else {
100+
print("Succes:")
101+
do {
102+
let parsedData = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
103+
if let theAccessToken = parsedData["access_token"] as? String {
104+
let accessToken = theAccessToken
105+
_ = KeychainManager.save(key: "accessToken", data: accessToken)
106+
completion(accessToken)
107+
}
108+
} catch let error as NSError {
109+
print(error)
110+
}
111+
}
112+
}
113+
dataTask.resume()
114+
115+
}
116+
117+
public func query(_ parameters: [String: String]) -> String {
118+
var components: [(String, String)] = []
119+
120+
for key in parameters.keys.sorted(by: <) {
121+
let value = parameters[key]!
122+
components += [(key, escape(value))]//queryComponents(fromKey: key, value: value)
123+
}
124+
125+
return components.map { "\($0)=\($1)" }.joined(separator: "&")
126+
}
127+
128+
/// Function that uri encodes strings
129+
///
130+
/// - Parameter string: un encoded uri query parameter
131+
/// - Returns: encoded parameter
132+
public func escape(_ string: String) -> String {
133+
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
134+
let subDelimitersToEncode = "!$&'()*+,;="
135+
136+
var allowedCharacterSet = CharacterSet.urlQueryAllowed
137+
allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
138+
139+
var escaped = ""
140+
141+
//==========================================================================================================
142+
//
143+
// Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few
144+
// hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no
145+
// longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more
146+
// info, please refer to:
147+
//
148+
// - https://github.com/Alamofire/Alamofire/issues/206
149+
//
150+
//==========================================================================================================
151+
152+
if #available(iOS 8.3, *) {
153+
escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
154+
} else {
155+
let batchSize = 50
156+
var index = string.startIndex
157+
158+
while index != string.endIndex {
159+
let startIndex = index
160+
let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
161+
let range = startIndex..<endIndex
162+
163+
let substring = string.substring(with: range)
164+
165+
escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring
166+
167+
index = endIndex
168+
}
169+
}
170+
171+
return escaped
172+
}
173+
174+
private func url(withPath path: String, with dict: [String: String]) -> URL {
175+
var components = URLComponents(string: oauthUrl.appendingPathComponent(path).absoluteString)!
176+
components.queryItems = dict.map({ k, v in URLQueryItem(name: k, value: v) })
177+
return components.url!
178+
}
179+
}
180+
181+
extension URL {
182+
var queryParameters: [String: String]? {
183+
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
184+
let queryItems = components.queryItems else {
185+
return nil
186+
}
187+
188+
var parameters = [String: String]()
189+
queryItems.forEach { parameters[$0.name] = $0.value }
190+
return parameters
191+
}
192+
}

GoInfoGame/GoInfoGame/UI/MapViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class MapViewController: UIHostingController<MapView> {
1212

1313
override func viewDidLoad() {
1414
super.viewDidLoad()
15+
self.title = ""
1516

1617
// Do any additional setup after loading the view.
1718

@@ -37,7 +38,7 @@ class MapViewController: UIHostingController<MapView> {
3738
@objc func profileButtonTapped() {
3839
let profileView = ProfileView()
3940
let hostingController = UIHostingController(rootView: profileView)
40-
present(hostingController, animated: true, completion: nil)
41+
navigationController?.pushViewController(hostingController, animated: true)
4142
}
4243

4344
@objc func widthDemoButtonTapped() {

0 commit comments

Comments
 (0)