diff --git a/Sources/IndieWebKit/IndieAuth/AuthenticationRequest.swift b/Sources/IndieWebKit/IndieAuth/AuthenticationRequest.swift index 5231825..9120c1c 100644 --- a/Sources/IndieWebKit/IndieAuth/AuthenticationRequest.swift +++ b/Sources/IndieWebKit/IndieAuth/AuthenticationRequest.swift @@ -7,7 +7,7 @@ import Foundation import CryptoSwift -import AuthenticationServices +//import AuthenticationServices public class AuthenticationRequest { @@ -19,18 +19,23 @@ public class AuthenticationRequest { private var codeChallenge: String? private let codeChallengeMethod = "S256" - private var url: URL? { + var url: URL? { var requestUrl = URLComponents(url: authorizationEndpoint, resolvingAgainstBaseURL: false) - requestUrl?.queryItems?.append(URLQueryItem(name: "me", value: profile.absoluteString)) - requestUrl?.queryItems?.append(URLQueryItem(name: "client_id", value: clientId.absoluteString)) - requestUrl?.queryItems?.append(URLQueryItem(name: "redirect_uri", value: redirectUri.absoluteString)) - requestUrl?.queryItems?.append(URLQueryItem(name: "state", value: state)) + var queryItems: [URLQueryItem] = [] + + queryItems.append(URLQueryItem(name: "me", value: profile.absoluteString)) + queryItems.append(URLQueryItem(name: "client_id", value: clientId.absoluteString)) + queryItems.append(URLQueryItem(name: "redirect_uri", value: redirectUri.absoluteString)) + queryItems.append(URLQueryItem(name: "state", value: state)) + queryItems.append(URLQueryItem(name: "response_type", value: "id")) if codeChallenge != nil { - requestUrl?.queryItems?.append(URLQueryItem(name: "code_challenge", value: codeChallenge)) - requestUrl?.queryItems?.append(URLQueryItem(name: "code_challenge_method", value: codeChallengeMethod)) + queryItems.append(URLQueryItem(name: "code_challenge_method", value: codeChallengeMethod)) + queryItems.append(URLQueryItem(name: "code_challenge", value: codeChallenge)) } + requestUrl?.queryItems = queryItems + return requestUrl?.url } @@ -47,34 +52,41 @@ public class AuthenticationRequest { } } - func start(completion: @escaping ((String?) -> ())) { + @available(iOS 12.0, macOS 10.15, *) + func start(completion: @escaping ((URL?) -> ())) { guard url != nil else { // TODO: Throw some type of error return } - ASWebAuthenticationSession(url: url!, callbackURLScheme: nil) { [weak self] responseUrl, error in - guard error == nil else { - // TODO: Throw some type of error - return - } - - guard responseUrl != nil else { - // TODO: Throw some type of error - return - } - - let authorizationCode = self?.parseResponse(responseUrl!) - guard authorizationCode != nil else { - // TODO: Throw an error because authorization code should not be nil - return - } - - verifyAuthenticationCode(authorizationCode!) - }.start() +// ASWebAuthenticationSession(url: url!, callbackURLScheme: nil) { [weak self] responseUrl, error in +// guard error == nil else { +// // TODO: Throw some type of error +// return +// } +// +// guard responseUrl != nil else { +// // TODO: Throw some type of error +// return +// } +// +// let authorizationCode = self?.parseResponse(responseUrl!) +// guard authorizationCode != nil else { +// // TODO: Throw an error because authorization code should not be nil +// return +// } +// +// self?.verifyAuthenticationCode(authorizationCode!) { [weak self] codeVerified in +// if (codeVerified) { +// completion(self?.profile) +// } else { +// completion(nil) +// } +// } +// }.start() } - private func parseResponse(_ responseUrl: URL) -> String { + func parseResponse(_ responseUrl: URL) -> String { let responseComponents = URLComponents(url: responseUrl, resolvingAgainstBaseURL: false) var state = "" var code = "" @@ -95,16 +107,71 @@ public class AuthenticationRequest { return code } - private func verifyAuthenticationCode(_ code: String) { - // TODO: Resume #5 here + func verifyAuthenticationCode(_ code: String, completion: @escaping ((Bool) -> Void)) { + + do { + let verificationRequest = try getVerificationRequest(with: code) + + URLSession.shared.dataTask(with: verificationRequest) { [weak self] body, response, error in + guard error == nil else { + // TODO: Throw error here + return + } + + // TODO: Check to make sure content type is application/json + + guard body != nil else { + // TODO: throw error here + return + } + + let responseProfile = try! JSONDecoder().decode([String:URL].self, from: body!) + + completion(self!.confirmVerificationResponse(responseProfile)) + + }.resume() + + } catch { + // TODO: Figure out how to report error + completion(false) + } + } + + func getVerificationRequest(with code: String) throws -> URLRequest { + var request = URLRequest(url: authorizationEndpoint) + request.httpMethod = "POST" + request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + request.addValue("application/json", forHTTPHeaderField: "Accept") + + let postBody = [ + "code": code, + "client_id": clientId.absoluteString, + "redirect_uri": redirectUri.absoluteString + ] + + try request.httpBody = JSONEncoder().encode(postBody) + + return request } - private func generateDefaultCodeChallenge() -> String? { - return Data(base64Encoded: randomString(length: 128).sha256())?.base64EncodedString() + func confirmVerificationResponse(_ responseProfile: [String:URL]) -> Bool { + guard responseProfile["me"] != nil else { + return false + } + + let meComponents = URLComponents(url: responseProfile["me"]!, resolvingAgainstBaseURL: false) + let profileComponents = URLComponents(url: profile, resolvingAgainstBaseURL: false) + + let validProfile = meComponents?.host == profileComponents?.host + + if (validProfile) { + profile = responseProfile["me"]! + } + + return validProfile } - private func randomString(length: Int) -> String { - let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~" - return String((0.. String? { + return Data(base64Encoded: String.randomString(length: 128).sha256())?.base64EncodedString() } } diff --git a/Sources/IndieWebKit/String.extension.swift b/Sources/IndieWebKit/String.extension.swift new file mode 100644 index 0000000..60ca538 --- /dev/null +++ b/Sources/IndieWebKit/String.extension.swift @@ -0,0 +1,24 @@ +// +// File.swift +// +// +// Created by ehinkle-ad on 6/11/19. +// + +import Foundation +extension String { + public static func randomString(length: Int) -> String { + let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~" + return randomString(length: length, from: letters) + } + + public static func randomAlphaNumericString(length: Int) -> String { + let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + return randomString(length: length, from: letters) + } + + public static func randomString(length: Int, from stringOptions: String) -> String { + let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~" + return String((0..