Created
August 12, 2017 19:57
-
-
Save khanlou/641e1549fe0ef20a0c063ac9f3aa6b9a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public struct Credentials { | |
public init(key: String, secret: String) { | |
self.key = key | |
self.secret = secret | |
} | |
public let key: String | |
public let secret: String | |
} | |
struct OAuthSignatureGenerator { | |
let consumerCredentials: Credentials | |
let credentials: Credentials | |
let request: URLRequest | |
let timestamp: Date | |
let nonce: String | |
let dataEncoding: String.Encoding = .utf8 | |
public enum OAuth { | |
static let version = "1.0" | |
static let signatureMethod = "HMAC-SHA1" | |
} | |
public init(consumerCredentials: Credentials, credentials: Credentials, request: URLRequest, timestamp: Date = Date(), nonce: String = UUID().uuidString) { | |
self.consumerCredentials = consumerCredentials | |
self.credentials = credentials | |
self.request = request | |
self.timestamp = timestamp | |
self.nonce = nonce | |
} | |
var method: String { | |
return request.httpMethod ?? "" | |
} | |
var url: URL { | |
guard let url = request.url else { fatalError("URL unwrapping in OAuthBehavior failed.")} | |
return url | |
} | |
var urlWithNoQueryParams: URL { | |
var urlComponents = URLComponents(string: url.absoluteString) | |
urlComponents?.queryItems = nil | |
guard let urlWithNoQueryParams = urlComponents?.url else { fatalError("URL unwrapping in OAuthBehavior failed.") } | |
return urlWithNoQueryParams | |
} | |
var bodyParameters: [String: Any]? { | |
do { | |
return try JSONSerialization.jsonObject(with: request.httpBody.unwrap()) as? [String: Any] | |
} catch { | |
return nil | |
} | |
} | |
var queryParameters: [String: Any]? { | |
return URLComponents(string: url.absoluteString)? | |
.queryItems? | |
.reduce([String: Any](), { dict, queryItem in | |
dict.dictionaryByAdding(queryItem.name, value: queryItem.value ?? "") | |
}) | |
} | |
var parameters: [String: Any] { | |
return bodyParameters ?? queryParameters ?? [:] | |
} | |
var authorizationParameters: [String: Any] { | |
return [ | |
"oauth_version": OAuth.version, | |
"oauth_signature_method": OAuth.signatureMethod, | |
"oauth_consumer_key": consumerCredentials.key, | |
"oauth_timestamp": String(Int(timestamp.timeIntervalSince1970)), | |
"oauth_nonce": nonce, | |
"oauth_token": credentials.key, | |
].merged(with: parameters.filter(byKey: { $0.hasPrefix("oauth_") })) | |
} | |
public var authorizationHeader: String { | |
let headerComponents = authorizationParameters | |
.dictionaryByAdding("oauth_signature", value: self.oauthSignature) | |
.urlEncodedQueryPairs(using: self.dataEncoding) | |
.map({ pair in "\(pair.0)=\"\(pair.1)\"" }) | |
.sorted() | |
.joined(separator: ", ") | |
return "OAuth " + headerComponents | |
} | |
var signingKey: String { | |
let tokenSecret = credentials.secret.urlEncodedString() | |
let encodedConsumerSecret = consumerCredentials.secret.urlEncodedString() | |
return "\(encodedConsumerSecret)&\(tokenSecret)" | |
} | |
var encodedParameterString: String { | |
return authorizationParameters | |
.merged(with: parameters) | |
.urlEncodedQueryString(using: dataEncoding) | |
.components(separatedBy: "&") | |
.sorted() | |
.joined(separator: "&") | |
.urlEncodedString() | |
} | |
var signatureBaseString: String { | |
return "\(method)&\(encodedURL)&\(encodedParameterString)" | |
} | |
var encodedURL: String { | |
return urlWithNoQueryParams.absoluteString.urlEncodedString() | |
} | |
public var oauthSignature: String { | |
do { | |
let hasher = CryptoHasher(method: .sha1, defaultKey: nil) | |
let bytes = try hasher.make(signatureBaseString.makeBytes(), key: signingKey.makeBytes()) | |
return AvailBase64Encoder.shared.encode(bytes).string | |
} catch let error { | |
fatalError(String(describing: error)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment