diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 4ba8a3933..8e28808ca 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -25,6 +25,9 @@ 7004C24D25B69207005E0AD9 /* ParseRoleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */; }; 700A8A662B4CC1E40087ADBE /* ParsePointerable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */; }; 700A8A682B4CC2700087ADBE /* ParsePointerable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */; }; + 700A8A6B2B51EBB10087ADBE /* ParseOTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A6A2B51EBB10087ADBE /* ParseOTP.swift */; }; + 700A8A6D2B51EBC20087ADBE /* ParseOTP+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A6C2B51EBC20087ADBE /* ParseOTP+async.swift */; }; + 700A8A6F2B51EBE10087ADBE /* ParseOTP+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A6E2B51EBE10087ADBE /* ParseOTP+combine.swift */; }; 700AFE03289C3508006C1CD9 /* ParseQueryCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700AFE02289C3508006C1CD9 /* ParseQueryCacheTests.swift */; }; 70110D52250680140091CC1D /* ParseConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70110D51250680140091CC1D /* ParseConstants.swift */; }; 70110D572506CE890091CC1D /* BaseParseInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70110D562506CE890091CC1D /* BaseParseInstallation.swift */; }; @@ -370,6 +373,9 @@ 7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseRoleTests.swift; sourceTree = ""; }; 700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+async.swift"; sourceTree = ""; }; 700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+combine.swift"; sourceTree = ""; }; + 700A8A6A2B51EBB10087ADBE /* ParseOTP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseOTP.swift; sourceTree = ""; }; + 700A8A6C2B51EBC20087ADBE /* ParseOTP+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseOTP+async.swift"; sourceTree = ""; }; + 700A8A6E2B51EBE10087ADBE /* ParseOTP+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseOTP+combine.swift"; sourceTree = ""; }; 700AFE02289C3508006C1CD9 /* ParseQueryCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseQueryCacheTests.swift; sourceTree = ""; }; 70110D51250680140091CC1D /* ParseConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConstants.swift; sourceTree = ""; }; 70110D562506CE890091CC1D /* BaseParseInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseParseInstallation.swift; sourceTree = ""; }; @@ -906,6 +912,16 @@ path = Protocols; sourceTree = ""; }; + 700A8A692B51EA8F0087ADBE /* ParseOTP */ = { + isa = PBXGroup; + children = ( + 700A8A6A2B51EBB10087ADBE /* ParseOTP.swift */, + 700A8A6C2B51EBC20087ADBE /* ParseOTP+async.swift */, + 700A8A6E2B51EBE10087ADBE /* ParseOTP+combine.swift */, + ); + path = ParseOTP; + sourceTree = ""; + }; 70110D5D250849B30091CC1D /* InternalObjects */ = { isa = PBXGroup; children = ( @@ -1116,13 +1132,14 @@ 70A2D81325B358FA001BEB7D /* 3rd Party */ = { isa = PBXGroup; children = ( - 7C4C0929285E745400F202C6 /* ParseInstagram */, 703B096126BF484E005A112F /* ParseApple */, 703B096426BF4896005A112F /* ParseFacebook */, 70F03A312780C7DD00E5AFB4 /* ParseGithub */, 70F03A212780B8D000E5AFB4 /* ParseGoogle */, + 7C4C0929285E745400F202C6 /* ParseInstagram */, 703B096226BF486C005A112F /* ParseLDAP */, 70F03A322780C7EB00E5AFB4 /* ParseLinkedIn */, + 700A8A692B51EA8F0087ADBE /* ParseOTP */, 7C55F9E52860CD48002A352D /* ParseSpotify */, 703B096326BF487E005A112F /* ParseTwitter */, ); @@ -1533,6 +1550,7 @@ 70B412B929801B8B00F706EA /* ParseHookTriggerRequestable.swift in Sources */, F97B465F24D9C7B500F4A88B /* KeychainStore.swift in Sources */, 70B4E0C12762F313004C9757 /* QueryWhere.swift in Sources */, + 700A8A6B2B51EBB10087ADBE /* ParseOTP.swift in Sources */, 70170A442656B02D0070C905 /* ParseAnalytics.swift in Sources */, 70110D52250680140091CC1D /* ParseConstants.swift in Sources */, 91B79AC326EE3A4E00073F2C /* API+NonParseBodyCommand.swift in Sources */, @@ -1560,6 +1578,7 @@ 7C4C093A285E9A3700F202C6 /* ParseInstagram+combine.swift in Sources */, 700395A325A119430052CB31 /* Operations.swift in Sources */, 91BB8FCF2690BA70005A6BA5 /* QueryObservable.swift in Sources */, + 700A8A6D2B51EBC20087ADBE /* ParseOTP+async.swift in Sources */, 70F03A232780BDE200E5AFB4 /* ParseGoogle.swift in Sources */, 705025E628514F36008D6624 /* ParsePushPayloadAny.swift in Sources */, 709A148228395ED100BF85E5 /* ParseSchema+async.swift in Sources */, @@ -1674,6 +1693,7 @@ 70D41D8028B520E200613510 /* ParseKeychainAccessGroup.swift in Sources */, 70385E762858E1000084D306 /* ParseHookFunctionable.swift in Sources */, 703B095326BF47FD005A112F /* ParseTwitter+async.swift in Sources */, + 700A8A6F2B51EBE10087ADBE /* ParseOTP+combine.swift in Sources */, 7051E2E22986F10D00ABFC7E /* SocketTasks.swift in Sources */, 70CE0ABC285F8FF900DAEA86 /* ParseTypeable.swift in Sources */, 70BC9890252A5B5C00FF3074 /* Objectable.swift in Sources */, diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+async.swift index 491d3fc4c..76f52275b 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+async.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+async.swift @@ -12,7 +12,7 @@ public extension ParseGitHub { // MARK: Async/Await /** - Login a `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Login a `ParseUser` *asynchronously* using GitHub authentication for login. - parameter id: The **id** from **GitHub**. - parameter accessToken: Required **access_token** from **GitHub**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -31,7 +31,7 @@ public extension ParseGitHub { } /** - Login a `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Login a `ParseUser` *asynchronously* using GitHub authentication for login. - parameter authData: Dictionary containing key/values. - returns: An instance of the logged in `ParseUser`. - throws: An error of type `ParseError`. @@ -49,7 +49,7 @@ public extension ParseGitHub { public extension ParseGitHub { /** - Link the *current* `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using GitHub authentication for login. - parameter id: The **id** from **GitHub**. - parameter accessToken: Required **access_token** from **GitHub**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -68,7 +68,7 @@ public extension ParseGitHub { } /** - Link the *current* `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using GitHub authentication for login. - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+combine.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+combine.swift index c173937f3..8f927890a 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+combine.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub+combine.swift @@ -13,7 +13,7 @@ import Combine public extension ParseGitHub { // MARK: Combine /** - Login a `ParseUser` *asynchronously* using GitHub authentication for graph API login. Publishes when complete. + Login a `ParseUser` *asynchronously* using GitHub authentication for login. Publishes when complete. - parameter id: The **id** from **GitHub**. - parameter accessToken: Required **access_token** from **GitHub**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -31,7 +31,7 @@ public extension ParseGitHub { } /** - Login a `ParseUser` *asynchronously* using GitHub authentication for graph API login. Publishes when complete. + Login a `ParseUser` *asynchronously* using GitHub authentication for login. Publishes when complete. - parameter authData: Dictionary containing key/values. - returns: A publisher that eventually produces a single value and then finishes or fails. */ @@ -47,7 +47,7 @@ public extension ParseGitHub { public extension ParseGitHub { /** - Link the *current* `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using GitHub authentication for login. Publishes when complete. - parameter id: The **id** from **GitHub**. - parameter accessToken: Required **access_token** from **GitHub**. @@ -66,7 +66,7 @@ public extension ParseGitHub { } /** - Link the *current* `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using GitHub authentication for login. Publishes when complete. - parameter authData: Dictionary containing key/values. - returns: A publisher that eventually produces a single value and then finishes or fails. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift index f170ee2dc..183f3490d 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift @@ -59,7 +59,7 @@ public struct ParseGitHub: ParseAuthentication { public extension ParseGitHub { /** - Login a `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Login a `ParseUser` *asynchronously* using GitHub authentication for login. - parameter id: The `GitHub id` from **GitHub**. - parameter accessToken: Required **access_token** from **GitHub**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -104,7 +104,7 @@ public extension ParseGitHub { public extension ParseGitHub { /** - Link the *current* `ParseUser` *asynchronously* using GitHub authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using GitHub authentication for login. - parameter id: The **id** from **GitHub**. - parameter accessToken: Required **access_token** from **GitHub**. - parameter options: A set of header options sent to the server. Defaults to an empty set. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+async.swift index 79b8a5042..cb0d64886 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+async.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+async.swift @@ -12,7 +12,7 @@ public extension ParseGoogle { // MARK: Async/Await /** - Login a `ParseUser` *asynchronously* using Google authentication for graph API login. + Login a `ParseUser` *asynchronously* using Google authentication for login. - parameter id: The **id** from **Google**. - parameter idToken: Optional **id_token** from **Google**. - parameter accessToken: Optional **access_token** from **Google**. @@ -34,7 +34,7 @@ public extension ParseGoogle { } /** - Login a `ParseUser` *asynchronously* using Google authentication for graph API login. + Login a `ParseUser` *asynchronously* using Google authentication for login. - parameter authData: Dictionary containing key/values. - returns: An instance of the logged in `ParseUser`. - throws: An error of type `ParseError`. @@ -52,7 +52,7 @@ public extension ParseGoogle { public extension ParseGoogle { /** - Link the *current* `ParseUser` *asynchronously* using Google authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using Google authentication for login. - parameter id: The **id** from **Google**. - parameter idToken: Optional **id_token** from **Google**. - parameter accessToken: Optional **access_token** from **Google**. @@ -74,7 +74,7 @@ public extension ParseGoogle { } /** - Link the *current* `ParseUser` *asynchronously* using Google authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using Google authentication for login. - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+combine.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+combine.swift index 02d20c492..3b44c643c 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+combine.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle+combine.swift @@ -13,7 +13,7 @@ import Combine public extension ParseGoogle { // MARK: Combine /** - Login a `ParseUser` *asynchronously* using Google authentication for graph API login. Publishes when complete. + Login a `ParseUser` *asynchronously* using Google authentication for login. Publishes when complete. - parameter id: The **id** from **Google**. - parameter idToken: Optional **id_token** from **Google**. - parameter accessToken: Optional **access_token** from **Google**. @@ -34,7 +34,7 @@ public extension ParseGoogle { } /** - Login a `ParseUser` *asynchronously* using Google authentication for graph API login. Publishes when complete. + Login a `ParseUser` *asynchronously* using Google authentication for login. Publishes when complete. - parameter authData: Dictionary containing key/values. - returns: A publisher that eventually produces a single value and then finishes or fails. */ @@ -50,7 +50,7 @@ public extension ParseGoogle { public extension ParseGoogle { /** - Link the *current* `ParseUser` *asynchronously* using Google authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using Google authentication for login. Publishes when complete. - parameter id: The **id** from **Google**. - parameter idToken: Optional **id_token** from **Google**. @@ -72,7 +72,7 @@ public extension ParseGoogle { } /** - Link the *current* `ParseUser` *asynchronously* using Google authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using Google authentication for login. Publishes when complete. - parameter authData: Dictionary containing key/values. - returns: A publisher that eventually produces a single value and then finishes or fails. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift index 1bae3a817..7ceb75009 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift @@ -68,7 +68,7 @@ public struct ParseGoogle: ParseAuthentication { public extension ParseGoogle { /** - Login a `ParseUser` *asynchronously* using Google authentication for graph API login. + Login a `ParseUser` *asynchronously* using Google authentication for login. - parameter id: The `id` from **Google**. - parameter idToken: Optional **id_token** from **Google**. - parameter accessToken: Optional **access_token** from **Google**. @@ -116,7 +116,7 @@ public extension ParseGoogle { public extension ParseGoogle { /** - Link the *current* `ParseUser` *asynchronously* using Google authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using Google authentication for login. - parameter id: The **id** from **Google**. - parameter idToken: Optional **id_token** from **Google**. - parameter accessToken: Optional **access_token** from **Google**. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+async.swift index 2c652c8b9..c7867287e 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+async.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+async.swift @@ -12,7 +12,7 @@ public extension ParseLinkedIn { // MARK: Async/Await /** - Login a `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Login a `ParseUser` *asynchronously* using LinkedIn authentication for login. - parameter id: The **id** from **LinkedIn**. - parameter accessToken: Required **access_token** from **LinkedIn**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -33,7 +33,7 @@ public extension ParseLinkedIn { } /** - Login a `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Login a `ParseUser` *asynchronously* using LinkedIn authentication for login. - parameter authData: Dictionary containing key/values. - returns: An instance of the logged in `ParseUser`. - throws: An error of type `ParseError`. @@ -51,7 +51,7 @@ public extension ParseLinkedIn { public extension ParseLinkedIn { /** - Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for login. - parameter id: The **id** from **LinkedIn**. - parameter accessToken: Required **access_token** from **LinkedIn**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -72,7 +72,7 @@ public extension ParseLinkedIn { } /** - Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for login. - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+combine.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+combine.swift index 7c3f3dab0..368ff0223 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+combine.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn+combine.swift @@ -13,7 +13,7 @@ import Combine public extension ParseLinkedIn { // MARK: Combine /** - Login a `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. Publishes when complete. + Login a `ParseUser` *asynchronously* using LinkedIn authentication for login. Publishes when complete. - parameter id: The **id** from **LinkedIn**. - parameter accessToken: Required **access_token** from **LinkedIn**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -33,7 +33,7 @@ public extension ParseLinkedIn { } /** - Login a `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. Publishes when complete. + Login a `ParseUser` *asynchronously* using LinkedIn authentication for login. Publishes when complete. - parameter authData: Dictionary containing key/values. - returns: A publisher that eventually produces a single value and then finishes or fails. */ @@ -49,7 +49,7 @@ public extension ParseLinkedIn { public extension ParseLinkedIn { /** - Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for login. Publishes when complete. - parameter id: The **id** from **LinkedIn**. - parameter accessToken: Required **access_token** from **LinkedIn**. @@ -70,7 +70,7 @@ public extension ParseLinkedIn { } /** - Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for login. Publishes when complete. - parameter authData: Dictionary containing key/values. - returns: A publisher that eventually produces a single value and then finishes or fails. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift index e4cc4d70e..a09d5fd70 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift @@ -63,7 +63,7 @@ public struct ParseLinkedIn: ParseAuthentication { public extension ParseLinkedIn { /** - Login a `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Login a `ParseUser` *asynchronously* using LinkedIn authentication for login. - parameter id: The `LinkedIn id` from **LinkedIn**. - parameter accessToken: Required **access_token** from **LinkedIn**. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -110,7 +110,7 @@ public extension ParseLinkedIn { public extension ParseLinkedIn { /** - Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for graph API login. + Link the *current* `ParseUser` *asynchronously* using LinkedIn authentication for login. - parameter id: The **id** from **LinkedIn**. - parameter accessToken: Required **access_token** from **LinkedIn**. - parameter options: A set of header options sent to the server. Defaults to an empty set. diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP+async.swift new file mode 100644 index 000000000..401d309a3 --- /dev/null +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP+async.swift @@ -0,0 +1,107 @@ +// +// ParseOTP+async.swift +// ParseSwift +// +// Created by Corey Baker on 1/12/24. +// Copyright © 2024 Network Reconnaissance Lab. All rights reserved. +// + +import Foundation + +public extension ParseOTP { + // MARK: Async/Await + + /** + Login a `ParseUser` *asynchronously* using OTP authentication for login. + - parameter id: The **id** from **OTP**. + - parameter idToken: Optional **id_token** from **OTP**. + - parameter accessToken: Optional **access_token** from **OTP**. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: An instance of the logged in `ParseUser`. + - throws: An error of type `ParseError`. + */ + func login( + id: String, + idToken: String? = nil, + accessToken: String? = nil, + options: API.Options = [] + ) async throws -> AuthenticatedUser { + try await withCheckedThrowingContinuation { continuation in + self.login( + id: id, + idToken: idToken, + accessToken: accessToken, + options: options, + completion: continuation.resume + ) + } + } + + /** + Login a `ParseUser` *asynchronously* using OTP authentication for login. + - parameter authData: Dictionary containing key/values. + - returns: An instance of the logged in `ParseUser`. + - throws: An error of type `ParseError`. + */ + func login( + authData: [String: String], + options: API.Options = [] + ) async throws -> AuthenticatedUser { + try await withCheckedThrowingContinuation { continuation in + self.login( + authData: authData, + options: options, + completion: continuation.resume + ) + } + } +} + +public extension ParseOTP { + + /** + Link the *current* `ParseUser` *asynchronously* using OTP authentication for login. + - parameter id: The **id** from **OTP**. + - parameter idToken: Optional **id_token** from **OTP**. + - parameter accessToken: Optional **access_token** from **OTP**. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: An instance of the logged in `ParseUser`. + - throws: An error of type `ParseError`. + */ + func link( + id: String, + idToken: String? = nil, + accessToken: String? = nil, + options: API.Options = [] + ) async throws -> AuthenticatedUser { + try await withCheckedThrowingContinuation { continuation in + self.link( + id: id, + idToken: idToken, + accessToken: accessToken, + options: options, + completion: continuation.resume + ) + } + } + + /** + Link the *current* `ParseUser` *asynchronously* using OTP authentication for login. + - parameter authData: Dictionary containing key/values. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: An instance of the logged in `ParseUser`. + - throws: An error of type `ParseError`. + */ + func link( + authData: [String: String], + options: API.Options = [] + ) async throws -> AuthenticatedUser { + try await withCheckedThrowingContinuation { continuation in + self.link( + authData: authData, + options: options, + completion: continuation.resume + ) + } + } +} diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP+combine.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP+combine.swift new file mode 100644 index 000000000..dd1129acf --- /dev/null +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP+combine.swift @@ -0,0 +1,111 @@ +// +// ParseOTP+combine.swift +// ParseSwift +// +// Created by Corey Baker on 1/12/24. +// Copyright © 2024 Network Reconnaissance Lab. All rights reserved. +// + +#if canImport(Combine) +import Foundation +import Combine + +public extension ParseOTP { + // MARK: Combine + /** + Login a `ParseUser` *asynchronously* using OTP authentication for login. Publishes when complete. + - parameter id: The **id** from **OTP**. + - parameter idToken: Optional **id_token** from **OTP**. + - parameter accessToken: Optional **access_token** from **OTP**. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces a single value and then finishes or fails. + */ + func loginPublisher( + id: String, + idToken: String? = nil, + accessToken: String? = nil, + options: API.Options = [] + ) -> Future { + Future { promise in + self.login( + id: id, + accessToken: accessToken, + + ) + self.login( + id: id, + idToken: idToken, + accessToken: accessToken, + options: options, + completion: promise + ) + } + } + + /** + Login a `ParseUser` *asynchronously* using OTP authentication for login. Publishes when complete. + - parameter authData: Dictionary containing key/values. + - returns: A publisher that eventually produces a single value and then finishes or fails. + */ + func loginPublisher( + authData: [String: String], + options: API.Options = [] + ) -> Future { + Future { promise in + self.login( + authData: authData, + options: options, + completion: promise + ) + } + } +} + +public extension ParseOTP { + /** + Link the *current* `ParseUser` *asynchronously* using OTP authentication for login. + Publishes when complete. + - parameter id: The **id** from **OTP**. + - parameter idToken: Optional **id_token** from **OTP**. + - parameter accessToken: Optional **access_token** from **OTP**. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces a single value and then finishes or fails. + */ + func linkPublisher( + id: String, + idToken: String? = nil, + accessToken: String? = nil, + options: API.Options = [] + ) -> Future { + Future { promise in + self.link( + id: id, + idToken: idToken, + accessToken: accessToken, + options: options, + completion: promise + ) + } + } + + /** + Link the *current* `ParseUser` *asynchronously* using OTP authentication for login. + Publishes when complete. + - parameter authData: Dictionary containing key/values. + - returns: A publisher that eventually produces a single value and then finishes or fails. + */ + func linkPublisher( + authData: [String: String], + options: API.Options = [] + ) -> Future { + Future { promise in + self.link( + authData: authData, + options: options, + completion: promise + ) + } + } +} + +#endif diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP.swift new file mode 100644 index 000000000..d769f2201 --- /dev/null +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseOTP/ParseOTP.swift @@ -0,0 +1,251 @@ +// +// ParseOTP.swift +// ParseSwift +// +// Created by Corey Baker on 1/12/24. +// Copyright © 2024 Network Reconnaissance Lab. All rights reserved. +// + +import Foundation + +// swiftlint:disable line_length + +/** + Provides utility functions for working with OTP User Authentication and `ParseUser`'s. + Be sure your Parse Server is configured for [sign in with OTP](https://docs.parseplatform.org/parse-server/guide/#otp-authdata). + For information on acquiring OTP sign-in credentials to use with `ParseOTP`, refer to [OTP's Documentation](https://developers.otp.com/identity/protocols/oauth2). + */ +public struct ParseOTP: ParseAuthentication { + + /// Adapter status. + enum Status: String, Codable { + case enabled, disabled + } + + /// Authentication keys required for OTP authentication. + enum AuthenticationKeys: String, Codable { + case secret + case token + case oldToken = "old" + case mobile + case status + + /// Properly makes an authData dictionary with the required keys. + /// - parameter token: Required token for the user. + /// - parameter secret: Optional secret for OTP. + /// - parameter old: Optional old token for OTP. + /// - parameter mobile: Optional mobile number for OTP. + /// - returns: authData dictionary. + func makeDictionary( + secret: String? = nil, + token: String? = nil, + oldToken: String? = nil, + mobile: String? = nil + ) -> [String: String] { + + var returnDictionary = [String: String]() + + if let secret = secret { + returnDictionary[AuthenticationKeys.secret.rawValue] = secret + } + + if let token = token { + returnDictionary[AuthenticationKeys.token.rawValue] = token + } + + if let oldToken = oldToken { + returnDictionary[AuthenticationKeys.oldToken.rawValue] = oldToken + } + + if let mobile = mobile { + returnDictionary[AuthenticationKeys.mobile.rawValue] = mobile + } + + return returnDictionary + } + + /// Verifies all mandatory keys are in authData. + /// - parameter authData: Dictionary containing key/values. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. + func verifyMandatoryKeys(authData: [String: String]) -> Bool { + authData[AuthenticationKeys.token.rawValue] != nil + || authData[AuthenticationKeys.mobile.rawValue] != nil + } + } + + public static var __type: String { // swiftlint:disable:this identifier_name + "mfa" + } + + public init() {} +} + +// MARK: Login +public extension ParseOTP { + + func login( + authData: [String: String], + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + callbackQueue.async { + completion( + .failure( + .init( + code: .otherCause, + message: "Login is not supported. Please use \"link(...)\"." + ) + ) + ) + } + } +} + +// MARK: Link +public extension ParseOTP { + + /** + Verify and/or reauthenticate a `ParseUser` token *asynchronously* using OTP enrollment. + - parameter token: The **token** from **OTP**. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default value of .main. + - parameter completion: The block to execute. + */ + func verify( + token: String, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + Task { + do { + let currentUser = try await AuthenticatedUser.current() + guard let potentialOTPAuthData = currentUser.authData?[Self.__type], + var otpAuthData = potentialOTPAuthData else { + let error = ParseError( + code: .otherCause, + message: "Logged in user is missing authData, did you link MFA before attempting to call \"verify()\"." + ) + completion(.failure(error)) + return + } + if let oldToken = otpAuthData[AuthenticationKeys.token.rawValue] { + otpAuthData[AuthenticationKeys.oldToken.rawValue] = oldToken + } + otpAuthData[AuthenticationKeys.token.rawValue] = token + link( + authData: otpAuthData, + options: options, + callbackQueue: callbackQueue, + completion: completion + ) + } catch { + let defaultError = ParseError( + code: .otherCause, + message: "Could not retrieve logged in user from Keychain", + swift: error + ) + let parseError = error as? ParseError ?? defaultError + completion(.failure(parseError)) + } + } + } + + /** + Link the *current* `ParseUser` *asynchronously* using OTP enrollment. + - parameter secret: The **secret** from **OTP**. + - parameter token: The **token** from **OTP**. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default value of .main. + - parameter completion: The block to execute. + */ + func link( + secret: String, + token: String, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + let otpAuthData = AuthenticationKeys + .token + .makeDictionary( + secret: secret, + token: token + ) + link( + authData: otpAuthData, + options: options, + callbackQueue: callbackQueue, + completion: completion + ) + } + + /** + Link the *current* `ParseUser` *asynchronously* using SMS OTP enrollment. + - parameter mobile: The **mobile** number from **OTP**. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default value of .main. + - parameter completion: The block to execute. + */ + func link( + mobile: String, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + let otpAuthData = AuthenticationKeys + .token + .makeDictionary( + mobile: mobile + ) + link( + authData: otpAuthData, + options: options, + callbackQueue: callbackQueue, + completion: completion + ) + } + + func link( + authData: [String: String], + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + guard AuthenticationKeys.token.verifyMandatoryKeys(authData: authData) else { + callbackQueue.async { + completion( + .failure( + .init( + code: .otherCause, + message: "Should have \"authData\" in consisting of keys \"id\", \"idToken\" or \"accessToken\"." + ) + ) + ) + } + return + } + AuthenticatedUser.link( + Self.__type, + authData: authData, + options: options, + callbackQueue: callbackQueue, + completion: completion + ) + } +} + +// MARK: 3rd Party Authentication - ParseOTP +public extension ParseUser { + + /// A otp `ParseUser`. + static var otp: ParseOTP { + ParseOTP() + } + + /// An otp `ParseUser`. + var otp: ParseOTP { + Self.otp + } +}