added iOS source code
[wl-app.git] / iOS / Pods / OAuthSwift / Sources / OAuthSwiftCredential.swift
1 //
2 //  OAuthSwiftCredential.swift
3 //  OAuthSwift
4 //
5 //  Created by Dongri Jin on 6/22/14.
6 //  Copyright (c) 2014 Dongri Jin. All rights reserved.
7 //
8 import Foundation
9
10 /// Allow to customize computed headers
11 public protocol OAuthSwiftCredentialHeadersFactory {
12     func make(_ url: URL, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, body: Data?) -> [String: String]
13 }
14
15 /// Allow to sign
16 // swiftlint:disable:next class_delegate_protocol
17 public protocol OAuthSwiftSignatureDelegate {
18     static func sign(hashMethod: OAuthSwiftHashMethod, key: Data, message: Data) -> Data?
19 }
20
21 // The hash method used.
22 public enum OAuthSwiftHashMethod: String {
23     case sha1
24     case none
25
26     func hash(data: Data) -> Data? {
27         switch self {
28         case .sha1:
29             let mac = SHA1(data).calculate()
30             return Data(bytes: UnsafePointer<UInt8>(mac), count: mac.count)
31         case .none:
32             return data
33         }
34     }
35 }
36
37 /// The credential for authentification
38 open class OAuthSwiftCredential: NSObject, NSSecureCoding, Codable {
39
40     public static let supportsSecureCoding = true
41
42     public enum Version: Codable {
43         case oauth1, oauth2
44
45         public var shortVersion: String {
46             switch self {
47             case .oauth1:
48                 return "1.0"
49             case .oauth2:
50                 return "2.0"
51             }
52         }
53
54         var toInt32: Int32 {
55             switch self {
56             case .oauth1:
57                 return 1
58             case .oauth2:
59                 return 2
60             }
61         }
62
63         init(_ value: Int32) {
64             switch value {
65             case 1:
66                 self = .oauth1
67             case 2:
68                 self = .oauth2
69             default:
70                 self = .oauth1
71             }
72         }
73
74         public func encode(to encoder: Encoder) throws {
75             var container = encoder.singleValueContainer()
76             try container.encode(self.toInt32)
77         }
78
79         public init(from decoder: Decoder) throws {
80             self.init(try decoder.singleValueContainer().decode(Int32.self))
81         }
82     }
83
84     public enum SignatureMethod: String {
85         case HMAC_SHA1 = "HMAC-SHA1"
86         case RSA_SHA1 = "RSA-SHA1"
87         case PLAINTEXT = "PLAINTEXT"
88
89         public static var delegates: [SignatureMethod: OAuthSwiftSignatureDelegate.Type] =
90             [HMAC_SHA1: HMAC.self]
91
92         var hashMethod: OAuthSwiftHashMethod {
93             switch self {
94             case .HMAC_SHA1, .RSA_SHA1:
95                 return .sha1
96             case .PLAINTEXT:
97                 return .none
98             }
99         }
100
101         func sign(key: Data, message: Data) -> Data? {
102             if let delegate = SignatureMethod.delegates[self] {
103                 return delegate.sign(hashMethod: self.hashMethod, key: key, message: message)
104             }
105             assert(self == .PLAINTEXT, "No signature method installed for \(self)")
106             return message
107         }
108
109     }
110
111     // MARK: attributes
112     open internal(set) var consumerKey = ""
113     open internal(set) var consumerSecret = ""
114     open var oauthToken = ""
115     open var oauthRefreshToken = ""
116     open var oauthTokenSecret = ""
117     open var oauthTokenExpiresAt: Date?
118     open internal(set) var oauthVerifier = ""
119     open var version: Version = .oauth1
120     open var signatureMethod: SignatureMethod = .HMAC_SHA1
121
122     /// hook to replace headers creation
123     open var headersFactory: OAuthSwiftCredentialHeadersFactory?
124
125     // MARK: init
126     override init() {
127     }
128
129     public init(consumerKey: String, consumerSecret: String) {
130         self.consumerKey = consumerKey
131         self.consumerSecret = consumerSecret
132     }
133
134     // MARK: NSCoding protocol
135     fileprivate struct NSCodingKeys {
136         static let bundleId = Bundle.main.bundleIdentifier
137             ?? Bundle(for: OAuthSwiftCredential.self).bundleIdentifier
138             ?? ""
139         static let base = bundleId + "."
140         static let consumerKey = base + "comsumer_key"
141         static let consumerSecret = base + "consumer_secret"
142         static let oauthToken = base + "oauth_token"
143         static let oauthRefreshToken = base + "oauth_refresh_token"
144         static let oauthTokenExpiresAt = base + "oauth_token_expires_at"
145         static let oauthTokenSecret = base + "oauth_token_secret"
146         static let oauthVerifier = base + "oauth_verifier"
147         static let version = base + "version"
148         static let signatureMethod = base + "signatureMethod"
149     }
150
151     /// Cannot declare a required initializer within an extension.
152     /// extension OAuthSwiftCredential: NSCoding {
153     public required convenience init?(coder decoder: NSCoder) {
154
155         guard let consumerKey = decoder
156             .decodeObject(of: NSString.self,
157                           forKey: NSCodingKeys.consumerKey) as String? else {
158             if #available(iOS 9, OSX 10.11, *) {
159                 let error = CocoaError.error(.coderValueNotFound)
160                 decoder.failWithError(error)
161             }
162             return nil
163         }
164
165         guard let consumerSecret = decoder
166             .decodeObject(of: NSString.self,
167                           forKey: NSCodingKeys.consumerSecret) as String? else {
168             if #available(iOS 9, OSX 10.11, *) {
169                 let error = CocoaError.error(.coderValueNotFound)
170                 decoder.failWithError(error)
171             }
172             return nil
173         }
174         self.init(consumerKey: consumerKey, consumerSecret: consumerSecret)
175
176         guard let oauthToken = decoder
177             .decodeObject(of: NSString.self,
178                           forKey: NSCodingKeys.oauthToken) as String? else {
179             if #available(iOS 9, OSX 10.11, *) {
180                 let error = CocoaError.error(.coderValueNotFound)
181                 decoder.failWithError(error)
182             }
183             return nil
184         }
185         self.oauthToken = oauthToken
186
187         guard let oauthRefreshToken = decoder
188             .decodeObject(of: NSString.self,
189                           forKey: NSCodingKeys.oauthRefreshToken) as String? else {
190             if #available(iOS 9, OSX 10.11, *) {
191                 let error = CocoaError.error(.coderValueNotFound)
192                 decoder.failWithError(error)
193             }
194             return nil
195         }
196         self.oauthRefreshToken = oauthRefreshToken
197
198         guard let oauthTokenSecret = decoder
199             .decodeObject(of: NSString.self,
200                           forKey: NSCodingKeys.oauthTokenSecret) as String? else {
201             if #available(iOS 9, OSX 10.11, *) {
202                 let error = CocoaError.error(.coderValueNotFound)
203                 decoder.failWithError(error)
204             }
205             return nil
206         }
207         self.oauthTokenSecret = oauthTokenSecret
208
209         guard let oauthVerifier = decoder
210             .decodeObject(of: NSString.self,
211                           forKey: NSCodingKeys.oauthVerifier) as String? else {
212             if #available(iOS 9, OSX 10.11, *) {
213                     let error = CocoaError.error(.coderValueNotFound)
214                     decoder.failWithError(error)
215             }
216             return nil
217         }
218         self.oauthVerifier = oauthVerifier
219
220         self.oauthTokenExpiresAt = decoder
221             .decodeObject(of: NSDate.self, forKey: NSCodingKeys.oauthTokenExpiresAt) as Date?
222         self.version = Version(decoder.decodeInt32(forKey: NSCodingKeys.version))
223         if case .oauth1 = version {
224             self.signatureMethod = SignatureMethod(rawValue: (decoder.decodeObject(of: NSString.self, forKey: NSCodingKeys.signatureMethod) as String?) ?? "HMAC_SHA1") ?? .HMAC_SHA1
225         }
226     }
227
228     open func encode(with coder: NSCoder) {
229         coder.encode(self.consumerKey, forKey: NSCodingKeys.consumerKey)
230         coder.encode(self.consumerSecret, forKey: NSCodingKeys.consumerSecret)
231         coder.encode(self.oauthToken, forKey: NSCodingKeys.oauthToken)
232         coder.encode(self.oauthRefreshToken, forKey: NSCodingKeys.oauthRefreshToken)
233         coder.encode(self.oauthTokenSecret, forKey: NSCodingKeys.oauthTokenSecret)
234         coder.encode(self.oauthVerifier, forKey: NSCodingKeys.oauthVerifier)
235         coder.encode(self.oauthTokenExpiresAt, forKey: NSCodingKeys.oauthTokenExpiresAt)
236         coder.encode(self.version.toInt32, forKey: NSCodingKeys.version)
237         if case .oauth1 = version {
238             coder.encode(self.signatureMethod.rawValue, forKey: NSCodingKeys.signatureMethod)
239         }
240     }
241     // } // End NSCoding extension
242
243     // MARK: Codable protocol
244     enum CodingKeys: String, CodingKey {
245         case consumerKey
246         case consumerSecret
247         case oauthToken
248         case oauthRefreshToken
249         case oauthTokenSecret
250         case oauthVerifier
251         case oauthTokenExpiresAt
252         case version
253         case signatureMethodRawValue
254     }
255
256     public func encode(to encoder: Encoder) throws {
257         var container = encoder.container(keyedBy: CodingKeys.self)
258         try container.encode(self.consumerKey, forKey: .consumerKey)
259         try container.encode(self.consumerSecret, forKey: .consumerSecret)
260         try container.encode(self.oauthToken, forKey: .oauthToken)
261         try container.encode(self.oauthRefreshToken, forKey: .oauthRefreshToken)
262         try container.encode(self.oauthTokenSecret, forKey: .oauthTokenSecret)
263         try container.encode(self.oauthVerifier, forKey: .oauthVerifier)
264         try container.encodeIfPresent(self.oauthTokenExpiresAt, forKey: .oauthTokenExpiresAt)
265         try container.encode(self.version, forKey: .version)
266         if case .oauth1 = version {
267             try container.encode(self.signatureMethod.rawValue, forKey: .signatureMethodRawValue)
268         }
269     }
270
271     public required convenience init(from decoder: Decoder) throws {
272         let container = try decoder.container(keyedBy: CodingKeys.self)
273
274         self.init()
275
276         self.consumerKey = try container.decode(String.self, forKey: .consumerKey)
277         self.consumerSecret = try container.decode(String.self, forKey: .consumerSecret)
278
279         self.oauthToken = try container.decode(type(of: self.oauthToken), forKey: .oauthToken)
280         self.oauthRefreshToken = try container.decode(type(of: self.oauthRefreshToken), forKey: .oauthRefreshToken)
281         self.oauthTokenSecret = try container.decode(type(of: self.oauthTokenSecret), forKey: .oauthTokenSecret)
282         self.oauthVerifier = try container.decode(type(of: self.oauthVerifier), forKey: .oauthVerifier)
283         self.oauthTokenExpiresAt = try container.decodeIfPresent(Date.self, forKey: .oauthTokenExpiresAt)
284         self.version = try container.decode(type(of: self.version), forKey: .version)
285
286         if case .oauth1 = version {
287             self.signatureMethod = SignatureMethod(rawValue: try container.decode(type(of: self.signatureMethod.rawValue), forKey: .signatureMethodRawValue))!
288         }
289     }
290
291     // MARK: functions
292     /// for OAuth1 parameters must contains sorted query parameters and url must not contains query parameters
293     open func makeHeaders(_ url: URL, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, body: Data? = nil) -> [String: String] {
294         if let factory = headersFactory {
295             return factory.make(url, method: method, parameters: parameters, body: body)
296         }
297         switch self.version {
298         case .oauth1:
299             return ["Authorization": self.authorizationHeader(method: method, url: url, parameters: parameters, body: body)]
300         case .oauth2:
301             return self.oauthToken.isEmpty ? [:] : ["Authorization": "Bearer \(self.oauthToken)"]
302         }
303     }
304
305     open func authorizationHeader(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil) -> String {
306         let timestamp = String(Int64(Date().timeIntervalSince1970))
307         let nonce = OAuthSwiftCredential.generateNonce()
308         return self.authorizationHeader(method: method, url: url, parameters: parameters, body: body, timestamp: timestamp, nonce: nonce)
309     }
310
311     open class func generateNonce() -> String {
312         let uuidString: String = UUID().uuidString
313         return uuidString[0..<8]
314     }
315
316     open func authorizationHeader(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil, timestamp: String, nonce: String) -> String {
317         assert(self.version == .oauth1)
318         let authorizationParameters = self.authorizationParametersWithSignature(method: method, url: url, parameters: parameters, body: body, timestamp: timestamp, nonce: nonce)
319
320         var parameterComponents = authorizationParameters.urlEncodedQuery.components(separatedBy: "&") as [String]
321         parameterComponents.sort { $0 < $1 }
322
323         var headerComponents = [String]()
324         for component in parameterComponents {
325             let subcomponent = component.components(separatedBy: "=") as [String]
326             if subcomponent.count == 2 {
327                 headerComponents.append("\(subcomponent[0])=\"\(subcomponent[1])\"")
328             }
329         }
330
331         return "OAuth " + headerComponents.joined(separator: ", ")
332     }
333
334     open func authorizationParametersWithSignature(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil) -> OAuthSwift.Parameters {
335         let timestamp = String(Int64(Date().timeIntervalSince1970))
336         let nonce = OAuthSwiftCredential.generateNonce()
337         return self.authorizationParametersWithSignature(method: method, url: url, parameters: parameters, body: body, timestamp: timestamp, nonce: nonce)
338     }
339
340     open func authorizationParametersWithSignature(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil, timestamp: String, nonce: String) -> OAuthSwift.Parameters {
341         var authorizationParameters = self.authorizationParameters(body, timestamp: timestamp, nonce: nonce)
342
343         for (key, value) in parameters {
344             if key.hasPrefix("oauth_") {
345                 authorizationParameters.updateValue(value, forKey: key)
346             }
347         }
348
349         let combinedParameters = authorizationParameters.join(parameters)
350
351         authorizationParameters["oauth_signature"] = self.signature(method: method, url: url, parameters: combinedParameters)
352
353         return authorizationParameters
354     }
355
356     open func authorizationParameters(_ body: Data?, timestamp: String, nonce: String) -> OAuthSwift.Parameters {
357         var authorizationParameters = OAuthSwift.Parameters()
358         authorizationParameters["oauth_version"] = self.version.shortVersion
359         authorizationParameters["oauth_signature_method"] =  self.signatureMethod.rawValue
360         authorizationParameters["oauth_consumer_key"] = self.consumerKey
361         authorizationParameters["oauth_timestamp"] = timestamp
362         authorizationParameters["oauth_nonce"] = nonce
363         if let b = body, let hash = self.signatureMethod.hashMethod.hash(data: b) {
364             authorizationParameters["oauth_body_hash"] = hash.base64EncodedString(options: [])
365         }
366
367         if !self.oauthToken.isEmpty {
368             authorizationParameters["oauth_token"] = self.oauthToken
369         }
370         return authorizationParameters
371     }
372
373     open func signature(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters) -> String {
374         let encodedTokenSecret = self.oauthTokenSecret.urlEncoded
375         let encodedConsumerSecret = self.consumerSecret.urlEncoded
376
377         let signingKey = "\(encodedConsumerSecret)&\(encodedTokenSecret)"
378
379         var parameterComponents = parameters.urlEncodedQuery.components(separatedBy: "&")
380         parameterComponents.sort {
381             let p0 = $0.components(separatedBy: "=")
382             let p1 = $1.components(separatedBy: "=")
383             if p0.first == p1.first { return p0.last ?? "" < p1.last ?? "" }
384             return p0.first ?? "" < p1.first ?? ""
385         }
386
387         let parameterString = parameterComponents.joined(separator: "&")
388         let encodedParameterString = parameterString.urlEncoded
389
390         let encodedURL = url.absoluteString.urlEncoded
391
392         let signatureBaseString = "\(method)&\(encodedURL)&\(encodedParameterString)"
393
394         let key = signingKey.data(using: .utf8)!
395         let msg = signatureBaseString.data(using: .utf8)!
396
397         let sha1 = self.signatureMethod.sign(key: key, message: msg)!
398         return sha1.base64EncodedString(options: [])
399     }
400
401     open func isTokenExpired() -> Bool {
402         if let expiresDate = oauthTokenExpiresAt {
403             return expiresDate <= Date()
404         }
405
406         // If no expires date is available we assume the token is still valid since it doesn't have an expiration date to check with.
407         return false
408     }
409
410     // MARK: Equatable
411
412     override open func isEqual(_ object: Any?) -> Bool {
413         guard let rhs = object as? OAuthSwiftCredential else {
414             return false
415         }
416         let lhs = self
417         return lhs.consumerKey == rhs.consumerKey
418             && lhs.consumerSecret == rhs.consumerSecret
419             && lhs.oauthToken == rhs.oauthToken
420             && lhs.oauthRefreshToken == rhs.oauthRefreshToken
421             && lhs.oauthTokenSecret == rhs.oauthTokenSecret
422             && lhs.oauthTokenExpiresAt == rhs.oauthTokenExpiresAt
423             && lhs.oauthVerifier == rhs.oauthVerifier
424             && lhs.version == rhs.version
425             && lhs.signatureMethod == rhs.signatureMethod
426     }
427
428 }