├── .gitignore
├── Cartfile
├── Cartfile.private
├── Cartfile.resolved
├── GAuth Example
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
└── ViewController.swift
├── GAuth tvOS Example
├── AppDelegate.swift
├── Assets.xcassets
│ ├── App Icon & Top Shelf Image.brandassets
│ │ ├── App Icon - Large.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Front.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ └── Middle.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ ├── App Icon - Small.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Front.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ └── Middle.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── Top Shelf Image.imageset
│ │ │ └── Contents.json
│ ├── Contents.json
│ └── LaunchImage.launchimage
│ │ └── Contents.json
├── Base.lproj
│ └── Main.storyboard
├── Info.plist
└── ViewController.swift
├── GAuth.podspec
├── GAuth.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── GAuth-iOS.xcscheme
│ ├── GAuth-tvOS.xcscheme
│ └── GAuth-watchOS.xcscheme
├── GAuth.xcworkspace
└── contents.xcworkspacedata
├── GAuthTests
├── GoogleAuthenticatorClientSpec.swift
├── GoogleAuthenticatorSpec.swift
├── Info.plist
└── JSON Responses
│ ├── authorize-valid.json
│ └── oauth-request-valid.json
├── LICENSE
├── README.md
├── Sources
├── G-Auth.h
├── GoogleAuthenticatorClient.swift
├── Info-iOS.plist
├── Info-tvOS.plist
├── Info-watchOS.plist
├── Models
│ ├── DeviceAuthorizationToken.swift
│ ├── GoogleAuthenticatorError.swift
│ └── OAuthIntegrationProtocols.swift
└── Utilities
│ ├── DispatchAfter.swift
│ ├── Extensions.swift
│ └── HTTPClient.swift
└── makefile
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | Carthage/Checkouts
52 | Carthage/Build
53 |
54 | # fastlane
55 | #
56 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
57 | # screenshots whenever they are needed.
58 | # For more information about the recommended setup visit:
59 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
60 |
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "antitypical/Result"
--------------------------------------------------------------------------------
/Cartfile.private:
--------------------------------------------------------------------------------
1 | github "Quick/Quick"
2 | github "Quick/Nimble"
3 | github "fabiomassimo/OAuthSwift" "master"
4 | github "AliSoftware/OHHTTPStubs"
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Quick/Nimble" "v4.0.1"
2 | github "fabiomassimo/OAuthSwift" "144cb0128616f9b21b78f7c70969e94453a4f218"
3 | github "Quick/Quick" "v0.9.2"
4 | github "antitypical/Result" "2.1.0"
5 | github "AliSoftware/OHHTTPStubs" "5.2.0"
6 |
--------------------------------------------------------------------------------
/GAuth Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // GoogleAuthenticatorExample
4 | //
5 | // Created by Fabio Milano on 28/03/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | import OAuthSwift
12 |
13 | @UIApplicationMain
14 | class AppDelegate: UIResponder, UIApplicationDelegate {
15 |
16 | var window: UIWindow?
17 |
18 |
19 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
20 | // Override point for customization after application launch.
21 | return true
22 | }
23 |
24 | func applicationWillResignActive(application: UIApplication) {
25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
26 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
27 | }
28 |
29 | func applicationDidEnterBackground(application: UIApplication) {
30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
32 | }
33 |
34 | func applicationWillEnterForeground(application: UIApplication) {
35 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
36 | }
37 |
38 | func applicationDidBecomeActive(application: UIApplication) {
39 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
40 | }
41 |
42 | func applicationWillTerminate(application: UIApplication) {
43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
44 | }
45 |
46 |
47 |
48 |
49 | func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
50 | OAuthSwift.handleOpenURL(url)
51 |
52 | return true
53 | }
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/GAuth Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "83.5x83.5",
66 | "scale" : "2x"
67 | }
68 | ],
69 | "info" : {
70 | "version" : 1,
71 | "author" : "xcode"
72 | }
73 | }
--------------------------------------------------------------------------------
/GAuth Example/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/GAuth Example/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/GAuth Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/GAuth Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // GoogleAuthenticatorExample
4 | //
5 | // Created by Fabio Milano on 28/03/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Result
11 | import GAuth
12 |
13 | class ViewController: UIViewController {
14 |
15 | private var authenticator = GoogleAuthenticatorClient(consumerKey: "***CONSUMER KEY***", consumerSecret:"***CONSUMER SECRET***", bundleIdentifier: NSBundle.mainBundle().bundleIdentifier!, scope: GoogleServiceScope.GoogleAnalyticsRead)
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 | }
20 |
21 | override func viewDidAppear(animated: Bool) {
22 | super.viewDidAppear(animated)
23 |
24 | authenticator.authorize { (result) in
25 | switch result {
26 | case .Success( _):
27 | print("I have it")
28 | case .Failure(let error):
29 | print(error)
30 | }
31 | }
32 | }
33 |
34 | override func didReceiveMemoryWarning() {
35 | super.didReceiveMemoryWarning()
36 | // Dispose of any resources that can be recreated.
37 | }
38 |
39 |
40 | }
41 |
42 | import OAuthSwift
43 |
44 | extension OAuthSwiftCredential: AuthorizedToken {
45 | public var accessToken: String {
46 | return self.oauth_token
47 | }
48 |
49 | public var refreshToken: String? {
50 | return self.oauth_refresh_token
51 | }
52 |
53 | public var expiresIn: NSTimeInterval? {
54 | return self.expiresIn
55 | }
56 |
57 | public var isExpired: Bool {
58 | return self.isTokenExpired()
59 | }
60 |
61 | public var scopes: [String]? {
62 | return nil
63 | }
64 | }
65 |
66 | extension NSError: GoogleAuthenticatorErrorAdapter {
67 | public var googleAuthenticatorError: GoogleAuthenticatorError {
68 | switch self.code {
69 | case OAuthSwiftErrorCode.GeneralError.rawValue:
70 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
71 | case OAuthSwiftErrorCode.TokenExpiredError.rawValue:
72 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
73 | case OAuthSwiftErrorCode.MissingStateError.rawValue:
74 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
75 | case OAuthSwiftErrorCode.StateNotEqualError.rawValue:
76 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
77 | case OAuthSwiftErrorCode.ServerError.rawValue:
78 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
79 | case OAuthSwiftErrorCode.EncodingError.rawValue:
80 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
81 | case OAuthSwiftErrorCode.AuthorizationPending.rawValue:
82 | return GoogleAuthenticatorError.AuthorizationPending
83 | default:
84 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
85 | }
86 | }
87 | }
88 |
89 | struct OAuth2SwiftGoogleAuthenticator: GoogleAuthenticatorOAuthClient {
90 | typealias OAuthClientType = OAuth2SwiftGoogleAuthenticator
91 | typealias TokenType = OAuthSwiftCredential
92 | typealias Failure = NSError
93 |
94 | let oauthClient: OAuth2Swift
95 |
96 | /// The client ID.
97 | let clientID: String
98 |
99 | /// The client secret.
100 | let clientSecret: String
101 |
102 | /// The authorize URL.
103 | let authorizeURL: NSURL
104 |
105 | /// The token URL.
106 | let tokenURL: NSURL?
107 |
108 | /// The redirect URL.
109 | let redirectURL: NSURL?
110 |
111 | /// The token
112 | let token: TokenType?
113 |
114 | /// The scopes.
115 | let scopes: [String]
116 |
117 | static func Google(clientID clientID: String, clientSecret: String, bundleIdentifier: String, scope: GoogleServiceScope) -> OAuth2SwiftGoogleAuthenticator {
118 | let oauthClient = OAuth2Swift(consumerKey: clientID, consumerSecret: clientSecret, authorizeUrl: AuthenticationConstants.AuthorizeUrl.rawValue, accessTokenUrl: AuthenticationConstants.AccessTokensUrl.rawValue, responseType: "code")
119 | oauthClient.allowMissingStateCheck = true
120 |
121 | return OAuth2SwiftGoogleAuthenticator(oauthClient: oauthClient, clientID: clientID, clientSecret: clientSecret, authorizeURL: AuthenticationConstants.AuthorizeUrl.URL, tokenURL: AuthenticationConstants.AccessTokensUrl.URL, redirectURL:nil, token: nil, scopes: [ scope.string() ] )
122 | }
123 |
124 | func googleAuthenticatorRefreshToken(completion: Result -> Void) {
125 | oauthClient.refreshToken { result in
126 | completion(result)
127 | }
128 | }
129 |
130 | func googleAuthenticatorAuthorize(completion: Result -> Void) {
131 | let callbackURL = NSURL(string: NSBundle.mainBundle().bundleIdentifier! + AuthenticationConstants.CallbackPostfix.rawValue)!
132 |
133 | oauthClient.authorizeWithCallbackURL(callbackURL, scope: scopes.joinWithSeparator(" "), state: "", success: { credential, response, parameters in
134 | completion(.Success(credential))
135 | }) { error in
136 | completion(.Failure(error))
137 | }
138 | }
139 |
140 | func googleAuthenticatorAuthorizeDeviceCode(deviceCode: String, completion: Result -> Void) {
141 | oauthClient.authorizeDeviceCode(deviceCode) { result in
142 | completion(result)
143 | }
144 | }
145 | }
146 |
147 | extension OAuth2Swift {
148 | public func refreshToken(completion: Result -> Void) {
149 | // Due to lack of a public function to renew the token we ask to authorize a dummy request in order to retrieve a renewed token from the `onTokenRenewal` closure.
150 | startAuthorizedRequest("http://requestToRefreshToken", method: OAuthSwiftHTTPRequest.Method.GET, parameters: [:], headers: nil, onTokenRenewal: { credential in
151 | completion(.Success(credential))
152 | }, success: { (data, response) in
153 | // completion block already called in previous onTokenRenewal closure.
154 | }) { error in
155 | completion(.Failure(error))
156 | }
157 | }
158 |
159 | public func authorizeDeviceCode(deviceCode: String, completion: Result -> Void) {
160 | authorizeDeviceToken(deviceCode, success: { credential in
161 | completion(.Success(credential))
162 | }) { error in
163 | completion(.Failure(error))
164 | }
165 | }
166 | }
167 |
168 |
169 |
--------------------------------------------------------------------------------
/GAuth tvOS Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // GoogleAuthenticatorTV Example
4 | //
5 | // Created by Fabio Milano on 13/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "size" : "1280x768",
5 | "idiom" : "tv",
6 | "filename" : "App Icon - Large.imagestack",
7 | "role" : "primary-app-icon"
8 | },
9 | {
10 | "size" : "400x240",
11 | "idiom" : "tv",
12 | "filename" : "App Icon - Small.imagestack",
13 | "role" : "primary-app-icon"
14 | },
15 | {
16 | "size" : "1920x720",
17 | "idiom" : "tv",
18 | "filename" : "Top Shelf Image.imageset",
19 | "role" : "top-shelf-image"
20 | }
21 | ],
22 | "info" : {
23 | "version" : 1,
24 | "author" : "xcode"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "landscape",
5 | "idiom" : "tv",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "9.0",
8 | "scale" : "1x"
9 | }
10 | ],
11 | "info" : {
12 | "version" : 1,
13 | "author" : "xcode"
14 | }
15 | }
--------------------------------------------------------------------------------
/GAuth tvOS Example/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
34 |
40 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/GAuth tvOS Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | arm64
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/GAuth tvOS Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // GoogleAuthenticatorTV Example
4 | //
5 | // Created by Fabio Milano on 13/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import GAuth
11 | import Result
12 |
13 | class ViewController: UIViewController {
14 | @IBOutlet weak var accessTokenLabel: UILabel!
15 | @IBOutlet weak var deviceCodeLabel: UILabel!
16 | @IBOutlet weak var verificationUrlLabel: UILabel!
17 |
18 | private var authenticator = GoogleAuthenticatorClient(consumerKey: "***CONSUMER KEY***", consumerSecret:"***CONSUMER SECRET***", bundleIdentifier: NSBundle.mainBundle().bundleIdentifier!, scope: GoogleServiceScope.GoogleAnalyticsRead)
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | accessTokenLabel.text = ""
23 | }
24 |
25 | override func viewDidAppear(animated: Bool) {
26 | super.viewDidAppear(animated)
27 |
28 | authenticator.authorizeDevice({ [unowned self] (verificationUrl, userCode) in
29 | self.verificationUrlLabel.text = verificationUrl.absoluteString
30 | self.deviceCodeLabel.text = userCode
31 | }) { [unowned self] (result) in
32 | switch result {
33 | case .Success():
34 | self.accessTokenLabel.textColor = UIColor.greenColor()
35 | self.accessTokenLabel.text = "Authorized"
36 | case .Failure(let error):
37 | self.accessTokenLabel.textColor = UIColor.redColor()
38 | switch error{
39 | case .DeviceVerificationFailed(let description):
40 | self.accessTokenLabel.text = description
41 | case .AuthorizationError(let description):
42 | self.accessTokenLabel.text = description
43 | case .InvalidAccessToken:
44 | self.accessTokenLabel.text = "Invalid access token"
45 | default:
46 | self.accessTokenLabel.text = "Loading..."
47 | }
48 |
49 | }
50 | }
51 | accessTokenLabel.text = "Loading..."
52 | }
53 |
54 | override func didReceiveMemoryWarning() {
55 | super.didReceiveMemoryWarning()
56 | // Dispose of any resources that can be recreated.
57 | }
58 |
59 |
60 | }
61 |
62 | import OAuthSwift
63 |
64 | extension OAuthSwiftCredential: AuthorizedToken {
65 | public var accessToken: String {
66 | return self.oauth_token
67 | }
68 |
69 | public var refreshToken: String? {
70 | return self.oauth_refresh_token
71 | }
72 |
73 | public var expiresIn: NSTimeInterval? {
74 | return self.expiresIn
75 | }
76 |
77 | public var isExpired: Bool {
78 | return self.isTokenExpired()
79 | }
80 |
81 | public var scopes: [String]? {
82 | return nil
83 | }
84 | }
85 |
86 | extension NSError: GoogleAuthenticatorErrorAdapter {
87 | public var googleAuthenticatorError: GoogleAuthenticatorError {
88 | switch self.code {
89 | case OAuthSwiftErrorCode.GeneralError.rawValue:
90 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
91 | case OAuthSwiftErrorCode.TokenExpiredError.rawValue:
92 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
93 | case OAuthSwiftErrorCode.MissingStateError.rawValue:
94 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
95 | case OAuthSwiftErrorCode.StateNotEqualError.rawValue:
96 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
97 | case OAuthSwiftErrorCode.ServerError.rawValue:
98 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
99 | case OAuthSwiftErrorCode.EncodingError.rawValue:
100 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
101 | case OAuthSwiftErrorCode.AuthorizationPending.rawValue:
102 | return GoogleAuthenticatorError.AuthorizationPending
103 | default:
104 | return GoogleAuthenticatorError.AuthorizationError(self.localizedDescription)
105 | }
106 | }
107 | }
108 |
109 | struct OAuth2SwiftGoogleAuthenticator: GoogleAuthenticatorOAuthClient {
110 | typealias OAuthClientType = OAuth2SwiftGoogleAuthenticator
111 | typealias TokenType = OAuthSwiftCredential
112 | typealias Failure = NSError
113 |
114 | let oauthClient: OAuth2Swift
115 |
116 | /// The client ID.
117 | let clientID: String
118 |
119 | /// The client secret.
120 | let clientSecret: String
121 |
122 | /// The authorize URL.
123 | let authorizeURL: NSURL
124 |
125 | /// The token URL.
126 | let tokenURL: NSURL?
127 |
128 | /// The redirect URL.
129 | let redirectURL: NSURL?
130 |
131 | /// The token
132 | let token: TokenType?
133 |
134 | /// The scopes.
135 | let scopes: [String]
136 |
137 | static func Google(clientID clientID: String, clientSecret: String, bundleIdentifier: String, scope: GoogleServiceScope) -> OAuth2SwiftGoogleAuthenticator {
138 | let oauthClient = OAuth2Swift(consumerKey: clientID, consumerSecret: clientSecret, authorizeUrl: AuthenticationConstants.AuthorizeUrl.rawValue, accessTokenUrl: AuthenticationConstants.AccessTokensUrl.rawValue, responseType: "http://oauth.net/grant_type/device/1.0")
139 |
140 | return OAuth2SwiftGoogleAuthenticator(oauthClient: oauthClient, clientID: clientID, clientSecret: clientSecret, authorizeURL: AuthenticationConstants.AuthorizeUrl.URL, tokenURL: AuthenticationConstants.AccessTokensUrl.URL, redirectURL:nil, token: nil, scopes: [ scope.string() ] )
141 | }
142 |
143 | func googleAuthenticatorRefreshToken(completion: Result -> Void) {
144 | oauthClient.refreshToken { result in
145 | completion(result)
146 | }
147 | }
148 |
149 | func googleAuthenticatorAuthorizeDeviceCode(deviceCode: String, completion: Result -> Void) {
150 | oauthClient.authorizeDeviceCode(deviceCode) { result in
151 | completion(result)
152 | }
153 | }
154 | }
155 |
156 | extension OAuth2Swift {
157 | public func refreshToken(completion: Result -> Void) {
158 | // Due to lack of a public function to renew the token we ask to authorize a dummy request in order to retrieve a renewed token from the `onTokenRenewal` closure.
159 | startAuthorizedRequest("http://requestToRefreshToken", method: OAuthSwiftHTTPRequest.Method.GET, parameters: [:], headers: nil, onTokenRenewal: { credential in
160 | completion(.Success(credential))
161 | }, success: { (data, response) in
162 | // completion block already called in previous onTokenRenewal closure.
163 | }) { error in
164 | completion(.Failure(error))
165 | }
166 | }
167 |
168 | public func authorizeDeviceCode(deviceCode: String, completion: Result -> Void) {
169 | authorizeDeviceToken(deviceCode, success: { credential in
170 | completion(.Success(credential))
171 | }) { error in
172 | completion(.Failure(error))
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/GAuth.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = "GAuth"
3 | spec.version = "0.2.0"
4 | spec.summary = "Authentication to Google services made easy."
5 | spec.homepage = "https://www.github.com/fabiomassimo/GAuth"
6 | spec.license = { type: 'MIT', file: 'LICENSE' }
7 | spec.authors = { "Fabio Milano" => 'fabio@touchwonders.com' }
8 | spec.social_media_url = "http://twitter.com/iamfabiomilano"
9 |
10 | spec.ios.deployment_target = '9.0'
11 | spec.tvos.deployment_target = '9.0'
12 | spec.watchos.deployment_target = '2.0'
13 |
14 | spec.requires_arc = true
15 | spec.source = { git: "https://github.com/fabiomassimo/GAuth.git", tag: "#{spec.version}", submodules: true }
16 | spec.source_files = "Sources/**/*.{h,swift}"
17 |
18 | spec.dependency "Result", "~> 2.1"
19 | end
--------------------------------------------------------------------------------
/GAuth.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E505F5BC1CA998BD003FD2A4 /* G-Auth.h in Headers */ = {isa = PBXBuildFile; fileRef = E505F5BB1CA998BD003FD2A4 /* G-Auth.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | E505F5CA1CA99914003FD2A4 /* GoogleAuthenticatorClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E505F5C71CA99914003FD2A4 /* GoogleAuthenticatorClient.swift */; };
12 | E505F5CB1CA99914003FD2A4 /* GoogleAuthenticatorClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E505F5C71CA99914003FD2A4 /* GoogleAuthenticatorClient.swift */; };
13 | E505F5CC1CA99914003FD2A4 /* GoogleAuthenticatorClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E505F5C71CA99914003FD2A4 /* GoogleAuthenticatorClient.swift */; };
14 | E505F5D61CA99A82003FD2A4 /* G-Auth.h in Headers */ = {isa = PBXBuildFile; fileRef = E505F5BB1CA998BD003FD2A4 /* G-Auth.h */; settings = {ATTRIBUTES = (Public, ); }; };
15 | E505F5D71CA99A83003FD2A4 /* G-Auth.h in Headers */ = {isa = PBXBuildFile; fileRef = E505F5BB1CA998BD003FD2A4 /* G-Auth.h */; settings = {ATTRIBUTES = (Public, ); }; };
16 | E53459F21CA91A72000990A6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E53459F11CA91A72000990A6 /* AppDelegate.swift */; };
17 | E53459F41CA91A72000990A6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E53459F31CA91A72000990A6 /* ViewController.swift */; };
18 | E53459F71CA91A72000990A6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E53459F51CA91A72000990A6 /* Main.storyboard */; };
19 | E53459F91CA91A72000990A6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E53459F81CA91A72000990A6 /* Assets.xcassets */; };
20 | E53459FC1CA91A72000990A6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E53459FA1CA91A72000990A6 /* LaunchScreen.storyboard */; };
21 | E5A1AE871D1369F000C3142F /* OAuthSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5A1AE861D1369F000C3142F /* OAuthSwift.framework */; };
22 | E5B6FE671D0CBF1100B9641D /* GoogleAuthenticatorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE661D0CBF1100B9641D /* GoogleAuthenticatorError.swift */; };
23 | E5B6FE681D0CBF1100B9641D /* GoogleAuthenticatorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE661D0CBF1100B9641D /* GoogleAuthenticatorError.swift */; };
24 | E5B6FE691D0CBF1100B9641D /* GoogleAuthenticatorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE661D0CBF1100B9641D /* GoogleAuthenticatorError.swift */; };
25 | E5B6FE731D0CC98C00B9641D /* GAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E505F5B91CA998BD003FD2A4 /* GAuth.framework */; };
26 | E5B6FE741D0CC98C00B9641D /* GAuth.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E505F5B91CA998BD003FD2A4 /* GAuth.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
27 | E5B6FE7B1D0CCF0400B9641D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE7A1D0CCF0400B9641D /* Extensions.swift */; };
28 | E5B6FE7C1D0CCF0400B9641D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE7A1D0CCF0400B9641D /* Extensions.swift */; };
29 | E5B6FE7D1D0CCF0400B9641D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE7A1D0CCF0400B9641D /* Extensions.swift */; };
30 | E5B6FE7F1D0CE63C00B9641D /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE7E1D0CE63C00B9641D /* HTTPClient.swift */; };
31 | E5B6FE801D0CE63C00B9641D /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE7E1D0CE63C00B9641D /* HTTPClient.swift */; };
32 | E5B6FE811D0CE63C00B9641D /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE7E1D0CE63C00B9641D /* HTTPClient.swift */; };
33 | E5B6FE831D0CE8B000B9641D /* DeviceAuthorizationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE821D0CE8B000B9641D /* DeviceAuthorizationToken.swift */; };
34 | E5B6FE841D0CE8B000B9641D /* DeviceAuthorizationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE821D0CE8B000B9641D /* DeviceAuthorizationToken.swift */; };
35 | E5B6FE851D0CE8B000B9641D /* DeviceAuthorizationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE821D0CE8B000B9641D /* DeviceAuthorizationToken.swift */; };
36 | E5B6FE871D0CF7C000B9641D /* DispatchAfter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE861D0CF7C000B9641D /* DispatchAfter.swift */; };
37 | E5B6FE881D0CF7C000B9641D /* DispatchAfter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE861D0CF7C000B9641D /* DispatchAfter.swift */; };
38 | E5B6FE891D0CF7C000B9641D /* DispatchAfter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE861D0CF7C000B9641D /* DispatchAfter.swift */; };
39 | E5B6FE911D0ED3BE00B9641D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE901D0ED3BE00B9641D /* AppDelegate.swift */; };
40 | E5B6FE931D0ED3BE00B9641D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FE921D0ED3BE00B9641D /* ViewController.swift */; };
41 | E5B6FE961D0ED3BE00B9641D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5B6FE941D0ED3BE00B9641D /* Main.storyboard */; };
42 | E5B6FE981D0ED3BE00B9641D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5B6FE971D0ED3BE00B9641D /* Assets.xcassets */; };
43 | E5B6FED91D0EDDCC00B9641D /* GAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E505F5AC1CA9986D003FD2A4 /* GAuth.framework */; };
44 | E5B6FEDA1D0EDDCC00B9641D /* GAuth.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E505F5AC1CA9986D003FD2A4 /* GAuth.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
45 | E5B6FEFA1D0EE30400B9641D /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5B6FEF81D0EE30400B9641D /* Result.framework */; };
46 | E5B6FEFE1D0EE42100B9641D /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5B6FEFC1D0EE42100B9641D /* Result.framework */; };
47 | E5B6FF021D0EE42D00B9641D /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5B6FF001D0EE42D00B9641D /* Result.framework */; };
48 | E5B6FF331D0EF1F400B9641D /* GoogleAuthenticatorClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FF321D0EF1F400B9641D /* GoogleAuthenticatorClientSpec.swift */; };
49 | E5B6FF351D0EF1F400B9641D /* GAuth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E505F5B91CA998BD003FD2A4 /* GAuth.framework */; };
50 | E5B6FF3E1D0EF31E00B9641D /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5B6FF3B1D0EF31E00B9641D /* Nimble.framework */; };
51 | E5B6FF3F1D0EF31E00B9641D /* OHHTTPStubs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5B6FF3C1D0EF31E00B9641D /* OHHTTPStubs.framework */; };
52 | E5B6FF401D0EF31E00B9641D /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5B6FF3D1D0EF31E00B9641D /* Quick.framework */; };
53 | E5B6FF431D0EFAB200B9641D /* authorize-valid.json in Resources */ = {isa = PBXBuildFile; fileRef = E5B6FF421D0EFAB200B9641D /* authorize-valid.json */; };
54 | E5B6FF451D0F000F00B9641D /* OAuthIntegrationProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FF441D0F000F00B9641D /* OAuthIntegrationProtocols.swift */; };
55 | E5B6FF461D0F000F00B9641D /* OAuthIntegrationProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FF441D0F000F00B9641D /* OAuthIntegrationProtocols.swift */; };
56 | E5B6FF471D0F000F00B9641D /* OAuthIntegrationProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B6FF441D0F000F00B9641D /* OAuthIntegrationProtocols.swift */; };
57 | E5DAAE8A1D1049780092169A /* GoogleAuthenticatorSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5DAAE891D1049780092169A /* GoogleAuthenticatorSpec.swift */; };
58 | /* End PBXBuildFile section */
59 |
60 | /* Begin PBXContainerItemProxy section */
61 | E5B6FE751D0CC98C00B9641D /* PBXContainerItemProxy */ = {
62 | isa = PBXContainerItemProxy;
63 | containerPortal = E5B77F331CA9121500F8BDD3 /* Project object */;
64 | proxyType = 1;
65 | remoteGlobalIDString = E505F5B81CA998BD003FD2A4;
66 | remoteInfo = "GoogleAuthenticator-iOS";
67 | };
68 | E5B6FEDB1D0EDDCC00B9641D /* PBXContainerItemProxy */ = {
69 | isa = PBXContainerItemProxy;
70 | containerPortal = E5B77F331CA9121500F8BDD3 /* Project object */;
71 | proxyType = 1;
72 | remoteGlobalIDString = E505F5AB1CA9986D003FD2A4;
73 | remoteInfo = "GoogleAuthenticator-tvOS";
74 | };
75 | E5B6FF361D0EF1F400B9641D /* PBXContainerItemProxy */ = {
76 | isa = PBXContainerItemProxy;
77 | containerPortal = E5B77F331CA9121500F8BDD3 /* Project object */;
78 | proxyType = 1;
79 | remoteGlobalIDString = E505F5B81CA998BD003FD2A4;
80 | remoteInfo = "GoogleAuthenticator-iOS";
81 | };
82 | /* End PBXContainerItemProxy section */
83 |
84 | /* Begin PBXCopyFilesBuildPhase section */
85 | E5B6FE6C1D0CC7EB00B9641D /* CopyFiles */ = {
86 | isa = PBXCopyFilesBuildPhase;
87 | buildActionMask = 2147483647;
88 | dstPath = "";
89 | dstSubfolderSpec = 10;
90 | files = (
91 | );
92 | runOnlyForDeploymentPostprocessing = 0;
93 | };
94 | E5B6FE771D0CC98D00B9641D /* Embed Frameworks */ = {
95 | isa = PBXCopyFilesBuildPhase;
96 | buildActionMask = 2147483647;
97 | dstPath = "";
98 | dstSubfolderSpec = 10;
99 | files = (
100 | E5B6FE741D0CC98C00B9641D /* GAuth.framework in Embed Frameworks */,
101 | );
102 | name = "Embed Frameworks";
103 | runOnlyForDeploymentPostprocessing = 0;
104 | };
105 | E5B6FEDD1D0EDDCC00B9641D /* Embed Frameworks */ = {
106 | isa = PBXCopyFilesBuildPhase;
107 | buildActionMask = 2147483647;
108 | dstPath = "";
109 | dstSubfolderSpec = 10;
110 | files = (
111 | E5B6FEDA1D0EDDCC00B9641D /* GAuth.framework in Embed Frameworks */,
112 | );
113 | name = "Embed Frameworks";
114 | runOnlyForDeploymentPostprocessing = 0;
115 | };
116 | /* End PBXCopyFilesBuildPhase section */
117 |
118 | /* Begin PBXFileReference section */
119 | E505F59F1CA9985F003FD2A4 /* GAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; };
120 | E505F5AC1CA9986D003FD2A4 /* GAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; };
121 | E505F5B91CA998BD003FD2A4 /* GAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; };
122 | E505F5BB1CA998BD003FD2A4 /* G-Auth.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "G-Auth.h"; sourceTree = ""; };
123 | E505F5C71CA99914003FD2A4 /* GoogleAuthenticatorClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleAuthenticatorClient.swift; sourceTree = ""; };
124 | E505F5D01CA9996D003FD2A4 /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; };
125 | E505F5D21CA99974003FD2A4 /* Info-tvOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = ""; };
126 | E505F5D41CA9997B003FD2A4 /* Info-watchOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-watchOS.plist"; sourceTree = ""; };
127 | E53459EF1CA91A72000990A6 /* GAuth Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GAuth Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
128 | E53459F11CA91A72000990A6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
129 | E53459F31CA91A72000990A6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
130 | E53459F61CA91A72000990A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
131 | E53459F81CA91A72000990A6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
132 | E53459FB1CA91A72000990A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
133 | E53459FD1CA91A72000990A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
134 | E5A1AE861D1369F000C3142F /* OAuthSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OAuthSwift.framework; path = Carthage/Build/tvOS/OAuthSwift.framework; sourceTree = SOURCE_ROOT; };
135 | E5B6FE661D0CBF1100B9641D /* GoogleAuthenticatorError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleAuthenticatorError.swift; sourceTree = ""; };
136 | E5B6FE7A1D0CCF0400B9641D /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
137 | E5B6FE7E1D0CE63C00B9641D /* HTTPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = ""; };
138 | E5B6FE821D0CE8B000B9641D /* DeviceAuthorizationToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceAuthorizationToken.swift; sourceTree = ""; };
139 | E5B6FE861D0CF7C000B9641D /* DispatchAfter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchAfter.swift; sourceTree = ""; };
140 | E5B6FE8E1D0ED3BD00B9641D /* GAuthTV Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GAuthTV Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
141 | E5B6FE901D0ED3BE00B9641D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
142 | E5B6FE921D0ED3BE00B9641D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
143 | E5B6FE951D0ED3BE00B9641D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
144 | E5B6FE971D0ED3BE00B9641D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
145 | E5B6FE991D0ED3BE00B9641D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
146 | E5B6FEF81D0EE30400B9641D /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/tvOS/Result.framework; sourceTree = SOURCE_ROOT; };
147 | E5B6FEFC1D0EE42100B9641D /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = SOURCE_ROOT; };
148 | E5B6FF001D0EE42D00B9641D /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/watchOS/Result.framework; sourceTree = SOURCE_ROOT; };
149 | E5B6FF301D0EF1F400B9641D /* GAuthTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GAuthTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
150 | E5B6FF321D0EF1F400B9641D /* GoogleAuthenticatorClientSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleAuthenticatorClientSpec.swift; sourceTree = ""; };
151 | E5B6FF341D0EF1F400B9641D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
152 | E5B6FF3B1D0EF31E00B9641D /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = SOURCE_ROOT; };
153 | E5B6FF3C1D0EF31E00B9641D /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OHHTTPStubs.framework; path = Carthage/Build/iOS/OHHTTPStubs.framework; sourceTree = SOURCE_ROOT; };
154 | E5B6FF3D1D0EF31E00B9641D /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = SOURCE_ROOT; };
155 | E5B6FF421D0EFAB200B9641D /* authorize-valid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "authorize-valid.json"; sourceTree = ""; };
156 | E5B6FF441D0F000F00B9641D /* OAuthIntegrationProtocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuthIntegrationProtocols.swift; sourceTree = ""; };
157 | E5DAAE891D1049780092169A /* GoogleAuthenticatorSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleAuthenticatorSpec.swift; sourceTree = ""; };
158 | /* End PBXFileReference section */
159 |
160 | /* Begin PBXFrameworksBuildPhase section */
161 | E505F59B1CA9985F003FD2A4 /* Frameworks */ = {
162 | isa = PBXFrameworksBuildPhase;
163 | buildActionMask = 2147483647;
164 | files = (
165 | E5B6FF021D0EE42D00B9641D /* Result.framework in Frameworks */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | E505F5A81CA9986D003FD2A4 /* Frameworks */ = {
170 | isa = PBXFrameworksBuildPhase;
171 | buildActionMask = 2147483647;
172 | files = (
173 | E5B6FEFA1D0EE30400B9641D /* Result.framework in Frameworks */,
174 | );
175 | runOnlyForDeploymentPostprocessing = 0;
176 | };
177 | E505F5B51CA998BD003FD2A4 /* Frameworks */ = {
178 | isa = PBXFrameworksBuildPhase;
179 | buildActionMask = 2147483647;
180 | files = (
181 | E5B6FEFE1D0EE42100B9641D /* Result.framework in Frameworks */,
182 | );
183 | runOnlyForDeploymentPostprocessing = 0;
184 | };
185 | E53459EC1CA91A72000990A6 /* Frameworks */ = {
186 | isa = PBXFrameworksBuildPhase;
187 | buildActionMask = 2147483647;
188 | files = (
189 | E5B6FE731D0CC98C00B9641D /* GAuth.framework in Frameworks */,
190 | );
191 | runOnlyForDeploymentPostprocessing = 0;
192 | };
193 | E5B6FE8B1D0ED3BD00B9641D /* Frameworks */ = {
194 | isa = PBXFrameworksBuildPhase;
195 | buildActionMask = 2147483647;
196 | files = (
197 | E5B6FED91D0EDDCC00B9641D /* GAuth.framework in Frameworks */,
198 | E5A1AE871D1369F000C3142F /* OAuthSwift.framework in Frameworks */,
199 | );
200 | runOnlyForDeploymentPostprocessing = 0;
201 | };
202 | E5B6FF2D1D0EF1F400B9641D /* Frameworks */ = {
203 | isa = PBXFrameworksBuildPhase;
204 | buildActionMask = 2147483647;
205 | files = (
206 | E5B6FF351D0EF1F400B9641D /* GAuth.framework in Frameworks */,
207 | E5B6FF3E1D0EF31E00B9641D /* Nimble.framework in Frameworks */,
208 | E5B6FF3F1D0EF31E00B9641D /* OHHTTPStubs.framework in Frameworks */,
209 | E5B6FF401D0EF31E00B9641D /* Quick.framework in Frameworks */,
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | };
213 | /* End PBXFrameworksBuildPhase section */
214 |
215 | /* Begin PBXGroup section */
216 | E505F55E1CA9463D003FD2A4 /* Frameworks */ = {
217 | isa = PBXGroup;
218 | children = (
219 | E5A1AE861D1369F000C3142F /* OAuthSwift.framework */,
220 | E5B6FF3B1D0EF31E00B9641D /* Nimble.framework */,
221 | E5B6FF3C1D0EF31E00B9641D /* OHHTTPStubs.framework */,
222 | E5B6FF3D1D0EF31E00B9641D /* Quick.framework */,
223 | E5B6FF001D0EE42D00B9641D /* Result.framework */,
224 | E5B6FEFC1D0EE42100B9641D /* Result.framework */,
225 | E5B6FEF81D0EE30400B9641D /* Result.framework */,
226 | );
227 | path = Frameworks;
228 | sourceTree = "";
229 | };
230 | E505F5C61CA99914003FD2A4 /* Sources */ = {
231 | isa = PBXGroup;
232 | children = (
233 | E5B6FE791D0CCEF300B9641D /* Utilities */,
234 | E505F5BB1CA998BD003FD2A4 /* G-Auth.h */,
235 | E505F5D41CA9997B003FD2A4 /* Info-watchOS.plist */,
236 | E505F5D21CA99974003FD2A4 /* Info-tvOS.plist */,
237 | E505F5D01CA9996D003FD2A4 /* Info-iOS.plist */,
238 | E505F5C71CA99914003FD2A4 /* GoogleAuthenticatorClient.swift */,
239 | E505F5C81CA99914003FD2A4 /* Models */,
240 | );
241 | path = Sources;
242 | sourceTree = "";
243 | };
244 | E505F5C81CA99914003FD2A4 /* Models */ = {
245 | isa = PBXGroup;
246 | children = (
247 | E5B6FE661D0CBF1100B9641D /* GoogleAuthenticatorError.swift */,
248 | E5B6FE821D0CE8B000B9641D /* DeviceAuthorizationToken.swift */,
249 | E5B6FF441D0F000F00B9641D /* OAuthIntegrationProtocols.swift */,
250 | );
251 | path = Models;
252 | sourceTree = "";
253 | };
254 | E53459F01CA91A72000990A6 /* GAuth Example */ = {
255 | isa = PBXGroup;
256 | children = (
257 | E53459F11CA91A72000990A6 /* AppDelegate.swift */,
258 | E53459F31CA91A72000990A6 /* ViewController.swift */,
259 | E53459F51CA91A72000990A6 /* Main.storyboard */,
260 | E53459F81CA91A72000990A6 /* Assets.xcassets */,
261 | E53459FA1CA91A72000990A6 /* LaunchScreen.storyboard */,
262 | E53459FD1CA91A72000990A6 /* Info.plist */,
263 | );
264 | path = "GAuth Example";
265 | sourceTree = "";
266 | };
267 | E5B6FE791D0CCEF300B9641D /* Utilities */ = {
268 | isa = PBXGroup;
269 | children = (
270 | E5B6FE7A1D0CCF0400B9641D /* Extensions.swift */,
271 | E5B6FE7E1D0CE63C00B9641D /* HTTPClient.swift */,
272 | E5B6FE861D0CF7C000B9641D /* DispatchAfter.swift */,
273 | );
274 | path = Utilities;
275 | sourceTree = "";
276 | };
277 | E5B6FE8F1D0ED3BE00B9641D /* GAuth tvOS Example */ = {
278 | isa = PBXGroup;
279 | children = (
280 | E5B6FE901D0ED3BE00B9641D /* AppDelegate.swift */,
281 | E5B6FE921D0ED3BE00B9641D /* ViewController.swift */,
282 | E5B6FE941D0ED3BE00B9641D /* Main.storyboard */,
283 | E5B6FE971D0ED3BE00B9641D /* Assets.xcassets */,
284 | E5B6FE991D0ED3BE00B9641D /* Info.plist */,
285 | );
286 | path = "GAuth tvOS Example";
287 | sourceTree = "";
288 | };
289 | E5B6FF311D0EF1F400B9641D /* GAuthTests */ = {
290 | isa = PBXGroup;
291 | children = (
292 | E5B6FF411D0EFA4200B9641D /* JSON Responses */,
293 | E5B6FF321D0EF1F400B9641D /* GoogleAuthenticatorClientSpec.swift */,
294 | E5B6FF341D0EF1F400B9641D /* Info.plist */,
295 | E5DAAE891D1049780092169A /* GoogleAuthenticatorSpec.swift */,
296 | );
297 | path = GAuthTests;
298 | sourceTree = "";
299 | };
300 | E5B6FF411D0EFA4200B9641D /* JSON Responses */ = {
301 | isa = PBXGroup;
302 | children = (
303 | E5B6FF421D0EFAB200B9641D /* authorize-valid.json */,
304 | );
305 | path = "JSON Responses";
306 | sourceTree = "";
307 | };
308 | E5B77F321CA9121500F8BDD3 = {
309 | isa = PBXGroup;
310 | children = (
311 | E505F5C61CA99914003FD2A4 /* Sources */,
312 | E53459F01CA91A72000990A6 /* GAuth Example */,
313 | E5B6FE8F1D0ED3BE00B9641D /* GAuth tvOS Example */,
314 | E5B6FF311D0EF1F400B9641D /* GAuthTests */,
315 | E505F55E1CA9463D003FD2A4 /* Frameworks */,
316 | E5B77F3D1CA9121500F8BDD3 /* Products */,
317 | );
318 | sourceTree = "";
319 | };
320 | E5B77F3D1CA9121500F8BDD3 /* Products */ = {
321 | isa = PBXGroup;
322 | children = (
323 | E53459EF1CA91A72000990A6 /* GAuth Example.app */,
324 | E505F59F1CA9985F003FD2A4 /* GAuth.framework */,
325 | E505F5AC1CA9986D003FD2A4 /* GAuth.framework */,
326 | E505F5B91CA998BD003FD2A4 /* GAuth.framework */,
327 | E5B6FE8E1D0ED3BD00B9641D /* GAuthTV Example.app */,
328 | E5B6FF301D0EF1F400B9641D /* GAuthTests.xctest */,
329 | );
330 | name = Products;
331 | sourceTree = "";
332 | };
333 | /* End PBXGroup section */
334 |
335 | /* Begin PBXHeadersBuildPhase section */
336 | E505F59C1CA9985F003FD2A4 /* Headers */ = {
337 | isa = PBXHeadersBuildPhase;
338 | buildActionMask = 2147483647;
339 | files = (
340 | E505F5D71CA99A83003FD2A4 /* G-Auth.h in Headers */,
341 | );
342 | runOnlyForDeploymentPostprocessing = 0;
343 | };
344 | E505F5A91CA9986D003FD2A4 /* Headers */ = {
345 | isa = PBXHeadersBuildPhase;
346 | buildActionMask = 2147483647;
347 | files = (
348 | E505F5D61CA99A82003FD2A4 /* G-Auth.h in Headers */,
349 | );
350 | runOnlyForDeploymentPostprocessing = 0;
351 | };
352 | E505F5B61CA998BD003FD2A4 /* Headers */ = {
353 | isa = PBXHeadersBuildPhase;
354 | buildActionMask = 2147483647;
355 | files = (
356 | E505F5BC1CA998BD003FD2A4 /* G-Auth.h in Headers */,
357 | );
358 | runOnlyForDeploymentPostprocessing = 0;
359 | };
360 | /* End PBXHeadersBuildPhase section */
361 |
362 | /* Begin PBXNativeTarget section */
363 | E505F59E1CA9985F003FD2A4 /* GAuth-watchOS */ = {
364 | isa = PBXNativeTarget;
365 | buildConfigurationList = E505F5A61CA9985F003FD2A4 /* Build configuration list for PBXNativeTarget "GAuth-watchOS" */;
366 | buildPhases = (
367 | E505F59A1CA9985F003FD2A4 /* Sources */,
368 | E505F59B1CA9985F003FD2A4 /* Frameworks */,
369 | E505F59C1CA9985F003FD2A4 /* Headers */,
370 | E505F59D1CA9985F003FD2A4 /* Resources */,
371 | );
372 | buildRules = (
373 | );
374 | dependencies = (
375 | );
376 | name = "GAuth-watchOS";
377 | productName = "GoogleAuthenticator-watchOS";
378 | productReference = E505F59F1CA9985F003FD2A4 /* GAuth.framework */;
379 | productType = "com.apple.product-type.framework";
380 | };
381 | E505F5AB1CA9986D003FD2A4 /* GAuth-tvOS */ = {
382 | isa = PBXNativeTarget;
383 | buildConfigurationList = E505F5B11CA9986D003FD2A4 /* Build configuration list for PBXNativeTarget "GAuth-tvOS" */;
384 | buildPhases = (
385 | E505F5A71CA9986D003FD2A4 /* Sources */,
386 | E505F5A81CA9986D003FD2A4 /* Frameworks */,
387 | E505F5A91CA9986D003FD2A4 /* Headers */,
388 | E505F5AA1CA9986D003FD2A4 /* Resources */,
389 | );
390 | buildRules = (
391 | );
392 | dependencies = (
393 | );
394 | name = "GAuth-tvOS";
395 | productName = "GoogleAuthenticator-tvOS";
396 | productReference = E505F5AC1CA9986D003FD2A4 /* GAuth.framework */;
397 | productType = "com.apple.product-type.framework";
398 | };
399 | E505F5B81CA998BD003FD2A4 /* GAuth-iOS */ = {
400 | isa = PBXNativeTarget;
401 | buildConfigurationList = E505F5C21CA998BE003FD2A4 /* Build configuration list for PBXNativeTarget "GAuth-iOS" */;
402 | buildPhases = (
403 | E505F5B41CA998BD003FD2A4 /* Sources */,
404 | E505F5B51CA998BD003FD2A4 /* Frameworks */,
405 | E505F5B61CA998BD003FD2A4 /* Headers */,
406 | E505F5B71CA998BD003FD2A4 /* Resources */,
407 | E5B6FE6C1D0CC7EB00B9641D /* CopyFiles */,
408 | );
409 | buildRules = (
410 | );
411 | dependencies = (
412 | );
413 | name = "GAuth-iOS";
414 | productName = "GoogleAuthenticator-iOS";
415 | productReference = E505F5B91CA998BD003FD2A4 /* GAuth.framework */;
416 | productType = "com.apple.product-type.framework";
417 | };
418 | E53459EE1CA91A72000990A6 /* GAuthExample */ = {
419 | isa = PBXNativeTarget;
420 | buildConfigurationList = E5345A001CA91A72000990A6 /* Build configuration list for PBXNativeTarget "GAuthExample" */;
421 | buildPhases = (
422 | E53459EB1CA91A72000990A6 /* Sources */,
423 | E53459EC1CA91A72000990A6 /* Frameworks */,
424 | E53459ED1CA91A72000990A6 /* Resources */,
425 | E5B6FE771D0CC98D00B9641D /* Embed Frameworks */,
426 | E5B6FE781D0CCA0B00B9641D /* Copy Frameworks */,
427 | );
428 | buildRules = (
429 | );
430 | dependencies = (
431 | E5B6FE761D0CC98C00B9641D /* PBXTargetDependency */,
432 | );
433 | name = GAuthExample;
434 | productName = GoogleAuthenticatorExample;
435 | productReference = E53459EF1CA91A72000990A6 /* GAuth Example.app */;
436 | productType = "com.apple.product-type.application";
437 | };
438 | E5B6FE8D1D0ED3BD00B9641D /* GAuthTV Example */ = {
439 | isa = PBXNativeTarget;
440 | buildConfigurationList = E5B6FE9C1D0ED3BE00B9641D /* Build configuration list for PBXNativeTarget "GAuthTV Example" */;
441 | buildPhases = (
442 | E5B6FE8A1D0ED3BD00B9641D /* Sources */,
443 | E5B6FE8B1D0ED3BD00B9641D /* Frameworks */,
444 | E5B6FE8C1D0ED3BD00B9641D /* Resources */,
445 | E5B6FEC71D0EDC0A00B9641D /* Copy Frameworks */,
446 | E5B6FEDD1D0EDDCC00B9641D /* Embed Frameworks */,
447 | );
448 | buildRules = (
449 | );
450 | dependencies = (
451 | E5B6FEDC1D0EDDCC00B9641D /* PBXTargetDependency */,
452 | );
453 | name = "GAuthTV Example";
454 | productName = "GoogleAuthenticatorTV Example";
455 | productReference = E5B6FE8E1D0ED3BD00B9641D /* GAuthTV Example.app */;
456 | productType = "com.apple.product-type.application";
457 | };
458 | E5B6FF2F1D0EF1F400B9641D /* GAuthTests */ = {
459 | isa = PBXNativeTarget;
460 | buildConfigurationList = E5B6FF381D0EF1F400B9641D /* Build configuration list for PBXNativeTarget "GAuthTests" */;
461 | buildPhases = (
462 | E5B6FF2C1D0EF1F400B9641D /* Sources */,
463 | E5B6FF2D1D0EF1F400B9641D /* Frameworks */,
464 | E5B6FF2E1D0EF1F400B9641D /* Resources */,
465 | E5B6FF481D0F037400B9641D /* Copy Frameworks */,
466 | );
467 | buildRules = (
468 | );
469 | dependencies = (
470 | E5B6FF371D0EF1F400B9641D /* PBXTargetDependency */,
471 | );
472 | name = GAuthTests;
473 | productName = GoogleAuthenticatorTests;
474 | productReference = E5B6FF301D0EF1F400B9641D /* GAuthTests.xctest */;
475 | productType = "com.apple.product-type.bundle.unit-test";
476 | };
477 | /* End PBXNativeTarget section */
478 |
479 | /* Begin PBXProject section */
480 | E5B77F331CA9121500F8BDD3 /* Project object */ = {
481 | isa = PBXProject;
482 | attributes = {
483 | LastSwiftUpdateCheck = 0730;
484 | LastUpgradeCheck = 0730;
485 | ORGANIZATIONNAME = Touchwonders;
486 | TargetAttributes = {
487 | E505F59E1CA9985F003FD2A4 = {
488 | CreatedOnToolsVersion = 7.3;
489 | };
490 | E505F5AB1CA9986D003FD2A4 = {
491 | CreatedOnToolsVersion = 7.3;
492 | };
493 | E505F5B81CA998BD003FD2A4 = {
494 | CreatedOnToolsVersion = 7.3;
495 | };
496 | E53459EE1CA91A72000990A6 = {
497 | CreatedOnToolsVersion = 7.3;
498 | };
499 | E5B6FE8D1D0ED3BD00B9641D = {
500 | CreatedOnToolsVersion = 7.3;
501 | };
502 | E5B6FF2F1D0EF1F400B9641D = {
503 | CreatedOnToolsVersion = 7.3;
504 | };
505 | };
506 | };
507 | buildConfigurationList = E5B77F361CA9121500F8BDD3 /* Build configuration list for PBXProject "GAuth" */;
508 | compatibilityVersion = "Xcode 3.2";
509 | developmentRegion = English;
510 | hasScannedForEncodings = 0;
511 | knownRegions = (
512 | en,
513 | Base,
514 | );
515 | mainGroup = E5B77F321CA9121500F8BDD3;
516 | productRefGroup = E5B77F3D1CA9121500F8BDD3 /* Products */;
517 | projectDirPath = "";
518 | projectRoot = "";
519 | targets = (
520 | E505F59E1CA9985F003FD2A4 /* GAuth-watchOS */,
521 | E505F5AB1CA9986D003FD2A4 /* GAuth-tvOS */,
522 | E505F5B81CA998BD003FD2A4 /* GAuth-iOS */,
523 | E53459EE1CA91A72000990A6 /* GAuthExample */,
524 | E5B6FE8D1D0ED3BD00B9641D /* GAuthTV Example */,
525 | E5B6FF2F1D0EF1F400B9641D /* GAuthTests */,
526 | );
527 | };
528 | /* End PBXProject section */
529 |
530 | /* Begin PBXResourcesBuildPhase section */
531 | E505F59D1CA9985F003FD2A4 /* Resources */ = {
532 | isa = PBXResourcesBuildPhase;
533 | buildActionMask = 2147483647;
534 | files = (
535 | );
536 | runOnlyForDeploymentPostprocessing = 0;
537 | };
538 | E505F5AA1CA9986D003FD2A4 /* Resources */ = {
539 | isa = PBXResourcesBuildPhase;
540 | buildActionMask = 2147483647;
541 | files = (
542 | );
543 | runOnlyForDeploymentPostprocessing = 0;
544 | };
545 | E505F5B71CA998BD003FD2A4 /* Resources */ = {
546 | isa = PBXResourcesBuildPhase;
547 | buildActionMask = 2147483647;
548 | files = (
549 | );
550 | runOnlyForDeploymentPostprocessing = 0;
551 | };
552 | E53459ED1CA91A72000990A6 /* Resources */ = {
553 | isa = PBXResourcesBuildPhase;
554 | buildActionMask = 2147483647;
555 | files = (
556 | E53459FC1CA91A72000990A6 /* LaunchScreen.storyboard in Resources */,
557 | E53459F91CA91A72000990A6 /* Assets.xcassets in Resources */,
558 | E53459F71CA91A72000990A6 /* Main.storyboard in Resources */,
559 | );
560 | runOnlyForDeploymentPostprocessing = 0;
561 | };
562 | E5B6FE8C1D0ED3BD00B9641D /* Resources */ = {
563 | isa = PBXResourcesBuildPhase;
564 | buildActionMask = 2147483647;
565 | files = (
566 | E5B6FE981D0ED3BE00B9641D /* Assets.xcassets in Resources */,
567 | E5B6FE961D0ED3BE00B9641D /* Main.storyboard in Resources */,
568 | );
569 | runOnlyForDeploymentPostprocessing = 0;
570 | };
571 | E5B6FF2E1D0EF1F400B9641D /* Resources */ = {
572 | isa = PBXResourcesBuildPhase;
573 | buildActionMask = 2147483647;
574 | files = (
575 | E5B6FF431D0EFAB200B9641D /* authorize-valid.json in Resources */,
576 | );
577 | runOnlyForDeploymentPostprocessing = 0;
578 | };
579 | /* End PBXResourcesBuildPhase section */
580 |
581 | /* Begin PBXShellScriptBuildPhase section */
582 | E5B6FE781D0CCA0B00B9641D /* Copy Frameworks */ = {
583 | isa = PBXShellScriptBuildPhase;
584 | buildActionMask = 2147483647;
585 | files = (
586 | );
587 | inputPaths = (
588 | "$(SRCROOT)/Carthage/Build/iOS/Result.framework",
589 | "$(SRCROOT)/Carthage/Build/tvOS/OAuthSwift.framework",
590 | );
591 | name = "Copy Frameworks";
592 | outputPaths = (
593 | );
594 | runOnlyForDeploymentPostprocessing = 0;
595 | shellPath = /bin/sh;
596 | shellScript = "/usr/local/bin/carthage copy-frameworks";
597 | };
598 | E5B6FEC71D0EDC0A00B9641D /* Copy Frameworks */ = {
599 | isa = PBXShellScriptBuildPhase;
600 | buildActionMask = 2147483647;
601 | files = (
602 | );
603 | inputPaths = (
604 | "$(SRCROOT)/Carthage/Build/tvOS/Result.framework",
605 | "$(SRCROOT)/Carthage/Build/tvOS/OAuthSwift.framework",
606 | );
607 | name = "Copy Frameworks";
608 | outputPaths = (
609 | );
610 | runOnlyForDeploymentPostprocessing = 0;
611 | shellPath = /bin/sh;
612 | shellScript = "/usr/local/bin/carthage copy-frameworks";
613 | };
614 | E5B6FF481D0F037400B9641D /* Copy Frameworks */ = {
615 | isa = PBXShellScriptBuildPhase;
616 | buildActionMask = 2147483647;
617 | files = (
618 | );
619 | inputPaths = (
620 | "$(SRCROOT)/Carthage/Build/iOS/Nimble.framework",
621 | "$(SRCROOT)/Carthage/Build/iOS/Quick.framework",
622 | "$(SRCROOT)/Carthage/Build/iOS/OHHTTPStubs.framework",
623 | "$(SRCROOT)/Carthage/Build/iOS/Result.framework",
624 | );
625 | name = "Copy Frameworks";
626 | outputPaths = (
627 | );
628 | runOnlyForDeploymentPostprocessing = 0;
629 | shellPath = /bin/sh;
630 | shellScript = "/usr/local/bin/carthage copy-frameworks";
631 | };
632 | /* End PBXShellScriptBuildPhase section */
633 |
634 | /* Begin PBXSourcesBuildPhase section */
635 | E505F59A1CA9985F003FD2A4 /* Sources */ = {
636 | isa = PBXSourcesBuildPhase;
637 | buildActionMask = 2147483647;
638 | files = (
639 | E5B6FE871D0CF7C000B9641D /* DispatchAfter.swift in Sources */,
640 | E5B6FE831D0CE8B000B9641D /* DeviceAuthorizationToken.swift in Sources */,
641 | E505F5CA1CA99914003FD2A4 /* GoogleAuthenticatorClient.swift in Sources */,
642 | E5B6FE671D0CBF1100B9641D /* GoogleAuthenticatorError.swift in Sources */,
643 | E5B6FE7F1D0CE63C00B9641D /* HTTPClient.swift in Sources */,
644 | E5B6FF451D0F000F00B9641D /* OAuthIntegrationProtocols.swift in Sources */,
645 | E5B6FE7B1D0CCF0400B9641D /* Extensions.swift in Sources */,
646 | );
647 | runOnlyForDeploymentPostprocessing = 0;
648 | };
649 | E505F5A71CA9986D003FD2A4 /* Sources */ = {
650 | isa = PBXSourcesBuildPhase;
651 | buildActionMask = 2147483647;
652 | files = (
653 | E5B6FE881D0CF7C000B9641D /* DispatchAfter.swift in Sources */,
654 | E5B6FE841D0CE8B000B9641D /* DeviceAuthorizationToken.swift in Sources */,
655 | E505F5CB1CA99914003FD2A4 /* GoogleAuthenticatorClient.swift in Sources */,
656 | E5B6FE681D0CBF1100B9641D /* GoogleAuthenticatorError.swift in Sources */,
657 | E5B6FE801D0CE63C00B9641D /* HTTPClient.swift in Sources */,
658 | E5B6FF461D0F000F00B9641D /* OAuthIntegrationProtocols.swift in Sources */,
659 | E5B6FE7C1D0CCF0400B9641D /* Extensions.swift in Sources */,
660 | );
661 | runOnlyForDeploymentPostprocessing = 0;
662 | };
663 | E505F5B41CA998BD003FD2A4 /* Sources */ = {
664 | isa = PBXSourcesBuildPhase;
665 | buildActionMask = 2147483647;
666 | files = (
667 | E5B6FE891D0CF7C000B9641D /* DispatchAfter.swift in Sources */,
668 | E5B6FE851D0CE8B000B9641D /* DeviceAuthorizationToken.swift in Sources */,
669 | E505F5CC1CA99914003FD2A4 /* GoogleAuthenticatorClient.swift in Sources */,
670 | E5B6FE691D0CBF1100B9641D /* GoogleAuthenticatorError.swift in Sources */,
671 | E5B6FE811D0CE63C00B9641D /* HTTPClient.swift in Sources */,
672 | E5B6FF471D0F000F00B9641D /* OAuthIntegrationProtocols.swift in Sources */,
673 | E5B6FE7D1D0CCF0400B9641D /* Extensions.swift in Sources */,
674 | );
675 | runOnlyForDeploymentPostprocessing = 0;
676 | };
677 | E53459EB1CA91A72000990A6 /* Sources */ = {
678 | isa = PBXSourcesBuildPhase;
679 | buildActionMask = 2147483647;
680 | files = (
681 | E53459F41CA91A72000990A6 /* ViewController.swift in Sources */,
682 | E53459F21CA91A72000990A6 /* AppDelegate.swift in Sources */,
683 | );
684 | runOnlyForDeploymentPostprocessing = 0;
685 | };
686 | E5B6FE8A1D0ED3BD00B9641D /* Sources */ = {
687 | isa = PBXSourcesBuildPhase;
688 | buildActionMask = 2147483647;
689 | files = (
690 | E5B6FE931D0ED3BE00B9641D /* ViewController.swift in Sources */,
691 | E5B6FE911D0ED3BE00B9641D /* AppDelegate.swift in Sources */,
692 | );
693 | runOnlyForDeploymentPostprocessing = 0;
694 | };
695 | E5B6FF2C1D0EF1F400B9641D /* Sources */ = {
696 | isa = PBXSourcesBuildPhase;
697 | buildActionMask = 2147483647;
698 | files = (
699 | E5B6FF331D0EF1F400B9641D /* GoogleAuthenticatorClientSpec.swift in Sources */,
700 | E5DAAE8A1D1049780092169A /* GoogleAuthenticatorSpec.swift in Sources */,
701 | );
702 | runOnlyForDeploymentPostprocessing = 0;
703 | };
704 | /* End PBXSourcesBuildPhase section */
705 |
706 | /* Begin PBXTargetDependency section */
707 | E5B6FE761D0CC98C00B9641D /* PBXTargetDependency */ = {
708 | isa = PBXTargetDependency;
709 | target = E505F5B81CA998BD003FD2A4 /* GAuth-iOS */;
710 | targetProxy = E5B6FE751D0CC98C00B9641D /* PBXContainerItemProxy */;
711 | };
712 | E5B6FEDC1D0EDDCC00B9641D /* PBXTargetDependency */ = {
713 | isa = PBXTargetDependency;
714 | target = E505F5AB1CA9986D003FD2A4 /* GAuth-tvOS */;
715 | targetProxy = E5B6FEDB1D0EDDCC00B9641D /* PBXContainerItemProxy */;
716 | };
717 | E5B6FF371D0EF1F400B9641D /* PBXTargetDependency */ = {
718 | isa = PBXTargetDependency;
719 | target = E505F5B81CA998BD003FD2A4 /* GAuth-iOS */;
720 | targetProxy = E5B6FF361D0EF1F400B9641D /* PBXContainerItemProxy */;
721 | };
722 | /* End PBXTargetDependency section */
723 |
724 | /* Begin PBXVariantGroup section */
725 | E53459F51CA91A72000990A6 /* Main.storyboard */ = {
726 | isa = PBXVariantGroup;
727 | children = (
728 | E53459F61CA91A72000990A6 /* Base */,
729 | );
730 | name = Main.storyboard;
731 | sourceTree = "";
732 | };
733 | E53459FA1CA91A72000990A6 /* LaunchScreen.storyboard */ = {
734 | isa = PBXVariantGroup;
735 | children = (
736 | E53459FB1CA91A72000990A6 /* Base */,
737 | );
738 | name = LaunchScreen.storyboard;
739 | sourceTree = "";
740 | };
741 | E5B6FE941D0ED3BE00B9641D /* Main.storyboard */ = {
742 | isa = PBXVariantGroup;
743 | children = (
744 | E5B6FE951D0ED3BE00B9641D /* Base */,
745 | );
746 | name = Main.storyboard;
747 | sourceTree = "";
748 | };
749 | /* End PBXVariantGroup section */
750 |
751 | /* Begin XCBuildConfiguration section */
752 | E505F5A41CA9985F003FD2A4 /* Debug */ = {
753 | isa = XCBuildConfiguration;
754 | buildSettings = {
755 | APPLICATION_EXTENSION_API_ONLY = YES;
756 | DEFINES_MODULE = YES;
757 | DYLIB_COMPATIBILITY_VERSION = 1;
758 | DYLIB_CURRENT_VERSION = 1;
759 | DYLIB_INSTALL_NAME_BASE = "@rpath";
760 | FRAMEWORK_SEARCH_PATHS = (
761 | "$(inherited)",
762 | "$(PROJECT_DIR)/Carthage/Build/watchOS",
763 | );
764 | GCC_PREPROCESSOR_DEFINITIONS = (
765 | "DEBUG=1",
766 | "$(inherited)",
767 | );
768 | INFOPLIST_FILE = "Sources/Info-watchOS.plist";
769 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
770 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
771 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GAuth-watchOS";
772 | PRODUCT_NAME = GAuth;
773 | SDKROOT = watchos2.2;
774 | SKIP_INSTALL = YES;
775 | TARGETED_DEVICE_FAMILY = 4;
776 | WATCHOS_DEPLOYMENT_TARGET = 2.2;
777 | };
778 | name = Debug;
779 | };
780 | E505F5A51CA9985F003FD2A4 /* Release */ = {
781 | isa = XCBuildConfiguration;
782 | buildSettings = {
783 | APPLICATION_EXTENSION_API_ONLY = YES;
784 | DEFINES_MODULE = YES;
785 | DYLIB_COMPATIBILITY_VERSION = 1;
786 | DYLIB_CURRENT_VERSION = 1;
787 | DYLIB_INSTALL_NAME_BASE = "@rpath";
788 | FRAMEWORK_SEARCH_PATHS = (
789 | "$(inherited)",
790 | "$(PROJECT_DIR)/Carthage/Build/watchOS",
791 | );
792 | INFOPLIST_FILE = "Sources/Info-watchOS.plist";
793 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
794 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
795 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GAuth-watchOS";
796 | PRODUCT_NAME = GAuth;
797 | SDKROOT = watchos2.2;
798 | SKIP_INSTALL = YES;
799 | TARGETED_DEVICE_FAMILY = 4;
800 | WATCHOS_DEPLOYMENT_TARGET = 2.2;
801 | };
802 | name = Release;
803 | };
804 | E505F5B21CA9986D003FD2A4 /* Debug */ = {
805 | isa = XCBuildConfiguration;
806 | buildSettings = {
807 | APPLICATION_EXTENSION_API_ONLY = NO;
808 | DEFINES_MODULE = YES;
809 | DYLIB_COMPATIBILITY_VERSION = 1;
810 | DYLIB_CURRENT_VERSION = 1;
811 | DYLIB_INSTALL_NAME_BASE = "@rpath";
812 | FRAMEWORK_SEARCH_PATHS = (
813 | "$(inherited)",
814 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
815 | );
816 | GCC_PREPROCESSOR_DEFINITIONS = (
817 | "DEBUG=1",
818 | "$(inherited)",
819 | );
820 | INFOPLIST_FILE = "Sources/Info-tvOS.plist";
821 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
822 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
823 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GAuth-tvOS";
824 | PRODUCT_NAME = GAuth;
825 | SDKROOT = appletvos9.2;
826 | SKIP_INSTALL = YES;
827 | TARGETED_DEVICE_FAMILY = 3;
828 | TVOS_DEPLOYMENT_TARGET = 9.0;
829 | };
830 | name = Debug;
831 | };
832 | E505F5B31CA9986D003FD2A4 /* Release */ = {
833 | isa = XCBuildConfiguration;
834 | buildSettings = {
835 | APPLICATION_EXTENSION_API_ONLY = NO;
836 | DEFINES_MODULE = YES;
837 | DYLIB_COMPATIBILITY_VERSION = 1;
838 | DYLIB_CURRENT_VERSION = 1;
839 | DYLIB_INSTALL_NAME_BASE = "@rpath";
840 | FRAMEWORK_SEARCH_PATHS = (
841 | "$(inherited)",
842 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
843 | );
844 | INFOPLIST_FILE = "Sources/Info-tvOS.plist";
845 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
846 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
847 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GAuth-tvOS";
848 | PRODUCT_NAME = GAuth;
849 | SDKROOT = appletvos9.2;
850 | SKIP_INSTALL = YES;
851 | TARGETED_DEVICE_FAMILY = 3;
852 | TVOS_DEPLOYMENT_TARGET = 9.0;
853 | };
854 | name = Release;
855 | };
856 | E505F5C31CA998BE003FD2A4 /* Debug */ = {
857 | isa = XCBuildConfiguration;
858 | buildSettings = {
859 | APPLICATION_EXTENSION_API_ONLY = NO;
860 | DEFINES_MODULE = YES;
861 | DYLIB_COMPATIBILITY_VERSION = 1;
862 | DYLIB_CURRENT_VERSION = 1;
863 | DYLIB_INSTALL_NAME_BASE = "@rpath";
864 | FRAMEWORK_SEARCH_PATHS = (
865 | "$(inherited)",
866 | "$(PROJECT_DIR)/Carthage/Build/iOS",
867 | );
868 | GCC_PREPROCESSOR_DEFINITIONS = (
869 | "DEBUG=1",
870 | "$(inherited)",
871 | );
872 | INFOPLIST_FILE = "Sources/Info-iOS.plist";
873 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
874 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
875 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
876 | OTHER_SWIFT_FLAGS = "";
877 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GAuth-iOS";
878 | PRODUCT_NAME = GAuth;
879 | SDKROOT = iphoneos9.3;
880 | SKIP_INSTALL = YES;
881 | };
882 | name = Debug;
883 | };
884 | E505F5C41CA998BE003FD2A4 /* Release */ = {
885 | isa = XCBuildConfiguration;
886 | buildSettings = {
887 | APPLICATION_EXTENSION_API_ONLY = NO;
888 | DEFINES_MODULE = YES;
889 | DYLIB_COMPATIBILITY_VERSION = 1;
890 | DYLIB_CURRENT_VERSION = 1;
891 | DYLIB_INSTALL_NAME_BASE = "@rpath";
892 | FRAMEWORK_SEARCH_PATHS = (
893 | "$(inherited)",
894 | "$(PROJECT_DIR)/Carthage/Build/iOS",
895 | );
896 | GCC_PREPROCESSOR_DEFINITIONS = "";
897 | INFOPLIST_FILE = "Sources/Info-iOS.plist";
898 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
899 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
900 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
901 | OTHER_SWIFT_FLAGS = "";
902 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GAuth-iOS";
903 | PRODUCT_NAME = GAuth;
904 | SDKROOT = iphoneos9.3;
905 | SKIP_INSTALL = YES;
906 | };
907 | name = Release;
908 | };
909 | E53459FE1CA91A72000990A6 /* Debug */ = {
910 | isa = XCBuildConfiguration;
911 | buildSettings = {
912 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
913 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
914 | FRAMEWORK_SEARCH_PATHS = (
915 | "$(inherited)",
916 | "$(PROJECT_DIR)/Carthage/Build/iOS",
917 | );
918 | INFOPLIST_FILE = "GAuth Example/Info.plist";
919 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
920 | PRODUCT_BUNDLE_IDENTIFIER = com.touchwonders.gauthexample;
921 | PRODUCT_NAME = "GAuth Example";
922 | };
923 | name = Debug;
924 | };
925 | E53459FF1CA91A72000990A6 /* Release */ = {
926 | isa = XCBuildConfiguration;
927 | buildSettings = {
928 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
929 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
930 | FRAMEWORK_SEARCH_PATHS = (
931 | "$(inherited)",
932 | "$(PROJECT_DIR)/Carthage/Build/iOS",
933 | );
934 | INFOPLIST_FILE = "GAuth Example/Info.plist";
935 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
936 | PRODUCT_BUNDLE_IDENTIFIER = com.touchwonders.gauthexample;
937 | PRODUCT_NAME = "GAuth Example";
938 | };
939 | name = Release;
940 | };
941 | E5B6FE9A1D0ED3BE00B9641D /* Debug */ = {
942 | isa = XCBuildConfiguration;
943 | buildSettings = {
944 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
945 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
946 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
947 | FRAMEWORK_SEARCH_PATHS = (
948 | "$(inherited)",
949 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
950 | );
951 | INFOPLIST_FILE = "GAuth tvOS Example/Info.plist";
952 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
953 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GoogleAuthenticatorTV-Example";
954 | PRODUCT_NAME = "GAuthTV Example";
955 | SDKROOT = appletvos;
956 | TARGETED_DEVICE_FAMILY = 3;
957 | TVOS_DEPLOYMENT_TARGET = 9.2;
958 | };
959 | name = Debug;
960 | };
961 | E5B6FE9B1D0ED3BE00B9641D /* Release */ = {
962 | isa = XCBuildConfiguration;
963 | buildSettings = {
964 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
965 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
966 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
967 | FRAMEWORK_SEARCH_PATHS = (
968 | "$(inherited)",
969 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
970 | );
971 | INFOPLIST_FILE = "GAuth tvOS Example/Info.plist";
972 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
973 | PRODUCT_BUNDLE_IDENTIFIER = "com.touchwonders.GoogleAuthenticatorTV-Example";
974 | PRODUCT_NAME = "GAuthTV Example";
975 | SDKROOT = appletvos;
976 | TARGETED_DEVICE_FAMILY = 3;
977 | TVOS_DEPLOYMENT_TARGET = 9.2;
978 | };
979 | name = Release;
980 | };
981 | E5B6FF391D0EF1F400B9641D /* Debug */ = {
982 | isa = XCBuildConfiguration;
983 | buildSettings = {
984 | FRAMEWORK_SEARCH_PATHS = (
985 | "$(inherited)",
986 | "$(PROJECT_DIR)/Carthage/Build/iOS",
987 | );
988 | INFOPLIST_FILE = GAuthTests/Info.plist;
989 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
990 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
991 | PRODUCT_BUNDLE_IDENTIFIER = com.touchwonders.GoogleAuthenticatorTests;
992 | PRODUCT_NAME = GAuthTests;
993 | SDKROOT = iphoneos;
994 | };
995 | name = Debug;
996 | };
997 | E5B6FF3A1D0EF1F400B9641D /* Release */ = {
998 | isa = XCBuildConfiguration;
999 | buildSettings = {
1000 | FRAMEWORK_SEARCH_PATHS = (
1001 | "$(inherited)",
1002 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1003 | );
1004 | INFOPLIST_FILE = GAuthTests/Info.plist;
1005 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
1006 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1007 | PRODUCT_BUNDLE_IDENTIFIER = com.touchwonders.GoogleAuthenticatorTests;
1008 | PRODUCT_NAME = GAuthTests;
1009 | SDKROOT = iphoneos;
1010 | };
1011 | name = Release;
1012 | };
1013 | E5B77F4E1CA9121500F8BDD3 /* Debug */ = {
1014 | isa = XCBuildConfiguration;
1015 | buildSettings = {
1016 | ALWAYS_SEARCH_USER_PATHS = NO;
1017 | APPLICATION_EXTENSION_API_ONLY = YES;
1018 | CLANG_ANALYZER_NONNULL = YES;
1019 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
1020 | CLANG_CXX_LIBRARY = "libc++";
1021 | CLANG_ENABLE_MODULES = YES;
1022 | CLANG_ENABLE_OBJC_ARC = YES;
1023 | CLANG_WARN_BOOL_CONVERSION = YES;
1024 | CLANG_WARN_CONSTANT_CONVERSION = YES;
1025 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
1026 | CLANG_WARN_EMPTY_BODY = YES;
1027 | CLANG_WARN_ENUM_CONVERSION = YES;
1028 | CLANG_WARN_INT_CONVERSION = YES;
1029 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
1030 | CLANG_WARN_UNREACHABLE_CODE = YES;
1031 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
1032 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
1033 | COPY_PHASE_STRIP = NO;
1034 | CURRENT_PROJECT_VERSION = 1;
1035 | DEBUG_INFORMATION_FORMAT = dwarf;
1036 | ENABLE_STRICT_OBJC_MSGSEND = YES;
1037 | ENABLE_TESTABILITY = YES;
1038 | GCC_C_LANGUAGE_STANDARD = gnu99;
1039 | GCC_DYNAMIC_NO_PIC = NO;
1040 | GCC_NO_COMMON_BLOCKS = YES;
1041 | GCC_OPTIMIZATION_LEVEL = 0;
1042 | GCC_PREPROCESSOR_DEFINITIONS = (
1043 | "DEBUG=1",
1044 | "$(inherited)",
1045 | );
1046 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
1047 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
1048 | GCC_WARN_UNDECLARED_SELECTOR = YES;
1049 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
1050 | GCC_WARN_UNUSED_FUNCTION = YES;
1051 | GCC_WARN_UNUSED_VARIABLE = YES;
1052 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
1053 | MTL_ENABLE_DEBUG_INFO = YES;
1054 | ONLY_ACTIVE_ARCH = YES;
1055 | OTHER_SWIFT_FLAGS = "";
1056 | SDKROOT = iphoneos9.3;
1057 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1058 | TARGETED_DEVICE_FAMILY = "1,2";
1059 | VERSIONING_SYSTEM = "apple-generic";
1060 | VERSION_INFO_PREFIX = "";
1061 | };
1062 | name = Debug;
1063 | };
1064 | E5B77F4F1CA9121500F8BDD3 /* Release */ = {
1065 | isa = XCBuildConfiguration;
1066 | buildSettings = {
1067 | ALWAYS_SEARCH_USER_PATHS = NO;
1068 | APPLICATION_EXTENSION_API_ONLY = YES;
1069 | CLANG_ANALYZER_NONNULL = YES;
1070 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
1071 | CLANG_CXX_LIBRARY = "libc++";
1072 | CLANG_ENABLE_MODULES = YES;
1073 | CLANG_ENABLE_OBJC_ARC = YES;
1074 | CLANG_WARN_BOOL_CONVERSION = YES;
1075 | CLANG_WARN_CONSTANT_CONVERSION = YES;
1076 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
1077 | CLANG_WARN_EMPTY_BODY = YES;
1078 | CLANG_WARN_ENUM_CONVERSION = YES;
1079 | CLANG_WARN_INT_CONVERSION = YES;
1080 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
1081 | CLANG_WARN_UNREACHABLE_CODE = YES;
1082 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
1083 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
1084 | COPY_PHASE_STRIP = NO;
1085 | CURRENT_PROJECT_VERSION = 1;
1086 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
1087 | ENABLE_NS_ASSERTIONS = NO;
1088 | ENABLE_STRICT_OBJC_MSGSEND = YES;
1089 | GCC_C_LANGUAGE_STANDARD = gnu99;
1090 | GCC_NO_COMMON_BLOCKS = YES;
1091 | GCC_PREPROCESSOR_DEFINITIONS = "";
1092 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
1093 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
1094 | GCC_WARN_UNDECLARED_SELECTOR = YES;
1095 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
1096 | GCC_WARN_UNUSED_FUNCTION = YES;
1097 | GCC_WARN_UNUSED_VARIABLE = YES;
1098 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
1099 | MTL_ENABLE_DEBUG_INFO = NO;
1100 | OTHER_SWIFT_FLAGS = "";
1101 | SDKROOT = iphoneos9.3;
1102 | TARGETED_DEVICE_FAMILY = "1,2";
1103 | VALIDATE_PRODUCT = YES;
1104 | VERSIONING_SYSTEM = "apple-generic";
1105 | VERSION_INFO_PREFIX = "";
1106 | };
1107 | name = Release;
1108 | };
1109 | /* End XCBuildConfiguration section */
1110 |
1111 | /* Begin XCConfigurationList section */
1112 | E505F5A61CA9985F003FD2A4 /* Build configuration list for PBXNativeTarget "GAuth-watchOS" */ = {
1113 | isa = XCConfigurationList;
1114 | buildConfigurations = (
1115 | E505F5A41CA9985F003FD2A4 /* Debug */,
1116 | E505F5A51CA9985F003FD2A4 /* Release */,
1117 | );
1118 | defaultConfigurationIsVisible = 0;
1119 | defaultConfigurationName = Release;
1120 | };
1121 | E505F5B11CA9986D003FD2A4 /* Build configuration list for PBXNativeTarget "GAuth-tvOS" */ = {
1122 | isa = XCConfigurationList;
1123 | buildConfigurations = (
1124 | E505F5B21CA9986D003FD2A4 /* Debug */,
1125 | E505F5B31CA9986D003FD2A4 /* Release */,
1126 | );
1127 | defaultConfigurationIsVisible = 0;
1128 | defaultConfigurationName = Release;
1129 | };
1130 | E505F5C21CA998BE003FD2A4 /* Build configuration list for PBXNativeTarget "GAuth-iOS" */ = {
1131 | isa = XCConfigurationList;
1132 | buildConfigurations = (
1133 | E505F5C31CA998BE003FD2A4 /* Debug */,
1134 | E505F5C41CA998BE003FD2A4 /* Release */,
1135 | );
1136 | defaultConfigurationIsVisible = 0;
1137 | defaultConfigurationName = Release;
1138 | };
1139 | E5345A001CA91A72000990A6 /* Build configuration list for PBXNativeTarget "GAuthExample" */ = {
1140 | isa = XCConfigurationList;
1141 | buildConfigurations = (
1142 | E53459FE1CA91A72000990A6 /* Debug */,
1143 | E53459FF1CA91A72000990A6 /* Release */,
1144 | );
1145 | defaultConfigurationIsVisible = 0;
1146 | defaultConfigurationName = Release;
1147 | };
1148 | E5B6FE9C1D0ED3BE00B9641D /* Build configuration list for PBXNativeTarget "GAuthTV Example" */ = {
1149 | isa = XCConfigurationList;
1150 | buildConfigurations = (
1151 | E5B6FE9A1D0ED3BE00B9641D /* Debug */,
1152 | E5B6FE9B1D0ED3BE00B9641D /* Release */,
1153 | );
1154 | defaultConfigurationIsVisible = 0;
1155 | defaultConfigurationName = Release;
1156 | };
1157 | E5B6FF381D0EF1F400B9641D /* Build configuration list for PBXNativeTarget "GAuthTests" */ = {
1158 | isa = XCConfigurationList;
1159 | buildConfigurations = (
1160 | E5B6FF391D0EF1F400B9641D /* Debug */,
1161 | E5B6FF3A1D0EF1F400B9641D /* Release */,
1162 | );
1163 | defaultConfigurationIsVisible = 0;
1164 | defaultConfigurationName = Release;
1165 | };
1166 | E5B77F361CA9121500F8BDD3 /* Build configuration list for PBXProject "GAuth" */ = {
1167 | isa = XCConfigurationList;
1168 | buildConfigurations = (
1169 | E5B77F4E1CA9121500F8BDD3 /* Debug */,
1170 | E5B77F4F1CA9121500F8BDD3 /* Release */,
1171 | );
1172 | defaultConfigurationIsVisible = 0;
1173 | defaultConfigurationName = Release;
1174 | };
1175 | /* End XCConfigurationList section */
1176 | };
1177 | rootObject = E5B77F331CA9121500F8BDD3 /* Project object */;
1178 | }
1179 |
--------------------------------------------------------------------------------
/GAuth.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/GAuth.xcodeproj/xcshareddata/xcschemes/GAuth-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/GAuth.xcodeproj/xcshareddata/xcschemes/GAuth-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/GAuth.xcodeproj/xcshareddata/xcschemes/GAuth-watchOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/GAuth.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/GAuthTests/GoogleAuthenticatorClientSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAuthenticatorClientSpec.swift
3 | // GoogleAuthenticatorSpec
4 | //
5 | // Created by Fabio Milano on 13/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import Nimble
10 | import Quick
11 | import OHHTTPStubs
12 | import Result
13 |
14 | @testable import GAuth
15 |
16 | class GoogleAuthenticatorClientSpec: QuickSpec {
17 |
18 | let bundle = NSBundle(forClass: GoogleAuthenticatorClient.self)
19 |
20 | override func spec(){
21 | describe("-authenticateRequest") {
22 | context("when successfully authenticate a NSURLRequest") {
23 | var authenticator: GoogleAuthenticatorClient?
24 |
25 | it("the request gets properly authenticated with valid access token") {
26 | authenticator = GoogleAuthenticatorClient(oauthClient: OAuthMockClient(tokenFeatures: TokenFeatures.Valid))
27 |
28 | let originalRequest = NSURLRequest(URL: NSURL(string: "http://googleauthenticator.touchwonders.com")!)
29 |
30 | waitUntil(action: { done in
31 | authenticator!.authenticateRequest(originalRequest, completion: { (result) in
32 | switch result {
33 | case .Success(let authenticatedRequest):
34 | expect(originalRequest.HTTPMethod).to(equal(authenticatedRequest.HTTPMethod))
35 | expect(originalRequest.HTTPBody).to(beNil())
36 | expect(originalRequest.cachePolicy).to(equal(authenticatedRequest.cachePolicy))
37 | expect(originalRequest.HTTPShouldHandleCookies).to(equal(authenticatedRequest.HTTPShouldHandleCookies))
38 | expect(originalRequest.HTTPBodyStream).to(beNil())
39 | expect(originalRequest.URL).to(equal(authenticatedRequest.URL))
40 | expect(authenticatedRequest.valueForHTTPHeaderField("Authorization")).toNot(beNil())
41 | case .Failure(let error):
42 | switch error {
43 | default:
44 | fail("Impossible to authorize the request")
45 | }
46 | }
47 |
48 | done()
49 | })
50 | })
51 | }
52 |
53 | it("the request get properly authenticated by first refreshing an expired token"){
54 | authenticator = GoogleAuthenticatorClient(oauthClient: OAuthMockClient(tokenFeatures: TokenFeatures.Expired))
55 |
56 | let originalRequest = NSURLRequest(URL: NSURL(string: "http://googleauthenticator.touchwonders.com")!)
57 |
58 | waitUntil(action: { (done) in
59 | authenticator!.authenticateRequest(originalRequest, completion: { (result) in
60 | switch result {
61 | case .Success(let authenticatedRequest):
62 | expect(originalRequest.HTTPMethod).to(equal(authenticatedRequest.HTTPMethod))
63 | expect(originalRequest.HTTPBody).to(beNil())
64 | expect(originalRequest.cachePolicy).to(equal(authenticatedRequest.cachePolicy))
65 | expect(originalRequest.HTTPShouldHandleCookies).to(equal(authenticatedRequest.HTTPShouldHandleCookies))
66 | expect(originalRequest.HTTPBodyStream).to(beNil())
67 | expect(originalRequest.URL).to(equal(authenticatedRequest.URL))
68 | expect(authenticatedRequest.valueForHTTPHeaderField("Authorization")).toNot(beNil())
69 | case .Failure(let error):
70 | switch error {
71 | default:
72 | fail("Impossible to authorize the request")
73 | }
74 | }
75 |
76 | done()
77 | })
78 | })
79 | }
80 |
81 | }
82 | }
83 |
84 | describe("-pollAccessToken") {
85 | context("when successfully retrieve a new access token") {
86 | let authenticator = GoogleAuthenticatorClient(oauthClient: OAuthMockClient(tokenFeatures: TokenFeatures.Valid))
87 |
88 |
89 | it("authorizes the user") {
90 | waitUntil(action: { done in
91 | authenticator.pollAccessToken("DEVICE_TOKEN", retryInterval: 0.5, completion: { result in
92 | switch result {
93 | case .Success( _):
94 | done()
95 | case .Failure(let error):
96 | fail("Impossible to authorize user: \(error)")
97 | }
98 | })
99 | })
100 | }
101 |
102 | it("authorizes the user after one retry") {
103 | // Make sure first request will fail with an authorization pending error
104 | authenticator.invalidateClientNextRequestWithError(GoogleAuthenticatorError.AuthorizationPending)
105 |
106 | waitUntil(timeout: 2.0, action: { done in
107 | authenticator.pollAccessToken("DEVICE_TOKEN", retryInterval: 0.5, completion: { result in
108 | switch result {
109 | case .Success( _):
110 | done()
111 | case .Failure(let error):
112 | fail("Impossible to authorize user: \(error)")
113 | }
114 | })
115 | })
116 | }
117 | }
118 | }
119 |
120 | describe("Helpers") {
121 | context("parameters") {
122 | let authenticator = GoogleAuthenticatorClient(oauthClient: OAuthMockClient(tokenFeatures: TokenFeatures.Valid))
123 |
124 | it("for device verification request") {
125 | let parameters = authenticator.parametersForDeviceVerification()
126 | expect(parameters["client_id"]).to(equal("CLIENT_ID"))
127 | expect(parameters["scope"]).to(equal("test_scope"))
128 | }
129 | it("for device authorization request") {
130 | let parameters = authenticator.parametersForDeviceAuthorization("TEST_CODE")
131 | expect(parameters["client_id"]).to(equal("CLIENT_ID"))
132 | expect(parameters["client_secret"]).to(equal("CLIENT_SECRET"))
133 | expect(parameters["code"]).to(equal("TEST_CODE"))
134 | expect(parameters["grant_type"]).to(equal("http://oauth.net/grant_type/device/1.0"))
135 | }
136 | }
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/GAuthTests/GoogleAuthenticatorSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAuthenticatorSpec.swift
3 | // GoogleAuthenticator
4 | //
5 | // Created by Fabio Milano on 14/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Result
11 |
12 | @testable import GAuth
13 |
14 | public struct TokenMock {
15 | public let accessToken: String
16 |
17 | public let refreshToken: String?
18 |
19 | public let expiresIn: NSTimeInterval?
20 |
21 | public let isExpired: Bool
22 |
23 | public let isValid: Bool
24 |
25 | public let scopes: [String]?
26 |
27 | static func expiredToken() -> TokenMock {
28 | return TokenMock(accessToken: "ACCESS_TOKEN", refreshToken: "REFRESH_TOKEN", expiresIn: 3600, isExpired: true, isValid: false, scopes: ["test_scope"])
29 | }
30 |
31 | static func validToken() -> TokenMock {
32 | return TokenMock(accessToken: "ACCESS_TOKEN", refreshToken: "REFRESH_TOKEN", expiresIn: 3600, isExpired: false, isValid: true, scopes: ["test_scope"])
33 | }
34 | }
35 |
36 | enum TokenFeatures {
37 | case Valid
38 | case Expired
39 | }
40 |
41 | public class OAuthMockClient {
42 |
43 | /// The client ID.
44 | public let clientID: String
45 |
46 | /// The client secret.
47 | public let clientSecret: String
48 |
49 | /// The authorize URL.
50 | public let authorizeURL: NSURL
51 |
52 | /// The token URL.
53 | public let tokenURL: NSURL?
54 |
55 | /// The redirect URL.
56 | public let redirectURL: NSURL?
57 |
58 | public let scopes: [String]
59 |
60 | public var token: TokenMock?
61 |
62 | init(tokenFeatures: TokenFeatures, clientID: String = "CLIENT_ID", clientSecret: String = "CLIENT_SECRET",
63 | authorizeURL: NSURL = NSURL(string: "http://authorizeurl.touchwonders.com")!, tokenURL: NSURL = NSURL(string: "http://tokenurl.touchwonders.com")!,
64 | redirectURL: NSURL = NSURL(string: "http://redirecturl.touchwonders.com")!,
65 | scopes: [String] = ["test_scope"]) {
66 | self.clientID = clientID
67 | self.clientSecret = clientSecret
68 | self.authorizeURL = authorizeURL
69 | self.tokenURL = tokenURL
70 | self.redirectURL = redirectURL
71 | self.scopes = scopes
72 |
73 | switch tokenFeatures {
74 | case .Valid:
75 | self.token = TokenMock.validToken()
76 | case .Expired:
77 | self.token = TokenMock.expiredToken()
78 | }
79 | }
80 |
81 | var nextOAuthClientError: GoogleAuthenticatorError?
82 | }
83 |
84 | extension TokenMock: AuthorizedToken { }
85 |
86 | extension GoogleAuthenticatorError: GoogleAuthenticatorErrorAdapter {
87 | public var googleAuthenticatorError: GoogleAuthenticatorError {
88 | return self
89 | }
90 | }
91 |
92 | extension GoogleAuthenticatorClient {
93 | public func invalidateClientNextRequestWithError(error: GoogleAuthenticatorError) -> Void {
94 | let client = oauthClient as! OAuthMockClient
95 | client.nextOAuthClientError = GoogleAuthenticatorError.AuthorizationPending
96 | }
97 | }
98 |
99 | extension OAuthMockClient: GoogleAuthenticatorOAuthClient {
100 | public typealias OAuthClientType = OAuthMockClient
101 | public typealias TokenType = TokenMock
102 | public typealias Failure = GoogleAuthenticatorError
103 |
104 | public static func Google(clientID clientID: String, clientSecret: String, bundleIdentifier: String, scope: GoogleServiceScope) -> OAuthMockClient {
105 | return OAuthMockClient(tokenFeatures: TokenFeatures.Valid)
106 | }
107 |
108 | public func googleAuthenticatorRefreshToken(completion: Result -> Void) {
109 |
110 | if let nextOAuthClientError = nextOAuthClientError {
111 | self.nextOAuthClientError = nil
112 | completion(.Failure(nextOAuthClientError))
113 | return
114 | }
115 |
116 | self.token = TokenMock.validToken()
117 | completion(.Success(token!))
118 | }
119 |
120 | public func googleAuthenticatorAuthorize(completion: Result -> Void) {
121 |
122 | if let nextOAuthClientError = nextOAuthClientError {
123 | self.nextOAuthClientError = nil
124 | completion(.Failure(nextOAuthClientError))
125 | return
126 | }
127 |
128 | completion(.Success(token!))
129 | }
130 |
131 | public func googleAuthenticatorAuthorizeDeviceCode(deviceCode: String, completion: Result -> Void) {
132 |
133 | if let nextOAuthClientError = nextOAuthClientError {
134 | self.nextOAuthClientError = nil
135 | completion(.Failure(nextOAuthClientError))
136 | return
137 | }
138 |
139 | completion(.Success(token!))
140 | }
141 | }
--------------------------------------------------------------------------------
/GAuthTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/GAuthTests/JSON Responses/authorize-valid.json:
--------------------------------------------------------------------------------
1 | {
2 | "access_token": "THE_STUB_ACCESS_TOKEN",
3 | "expires_in": 3600,
4 | "token_type": "bearer",
5 | "scope": "https://www.googleapis.com/auth/analytics.readonly",
6 | "refresh_token": "THE_STUB_REFRESH_TOKEN"
7 | }
--------------------------------------------------------------------------------
/GAuthTests/JSON Responses/oauth-request-valid.json:
--------------------------------------------------------------------------------
1 | {
2 | "access_token": "THE_STUB_ACCESS_TOKEN",
3 | "expires_in": 3600,
4 | "token_type": "bearer",
5 | "scope": "https://www.googleapis.com/auth/analytics.readonly",
6 | "refresh_token": "THE_STUB_REFRESH_TOKEN"
7 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Fabio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://twitter.com/iamfabiomilano)
2 | [](https://github.com/Carthage/Carthage)
3 | [](https://img.shields.io/cocoapods/v/GAuth.svg)
4 | [](http://cocoadocs.org/docsets/GAuth)
5 |
6 | # GAuth
7 |
8 | GAuth is small framework that makes authentication to Google Services easier by properly implementing the OAuth authentication flow described in [Google's Developers website](https://developers.google.com/identity/protocols/OAuth2InstalledApp).
9 |
10 | GAuth is not about to provide yet another Swift OAuth client but to easily integrate authentication to Google Services for the OAuth client already used in your project.
11 |
12 | # Cloning the repo
13 | To give `GAuth` a shot follow this steps:
14 |
15 | 1. Clone the repo.
16 | 2. Open the terminal and move to the `GAuth` folder.
17 | 3. To setup all required dependencies run `make setup`.
18 | 4. Open the `GAuth.xcworkspace`, build and run.
19 |
20 | # Installation
21 |
22 | Add via [CocoaPods](https://cocoapods.org/) by adding this to your Podfile:
23 |
24 | ```
25 | pod 'GAuth'
26 | ```
27 |
28 | If using [Carthage](https://github.com/Carthage/Carthage), add following line into your Cartfile:
29 |
30 | ```
31 | github "fabiomassimo/GAuth"
32 | ```
33 |
34 | # How to use it
35 |
36 | Integrate GAuth by making your OAuthClient ([OAuthSwift](https://github.com/OAuthSwift/OAuthSwift), [SwiftyOAuth](https://github.com/delba/SwiftyOAuth)) conform to `GoogleAuthenticatorOAuthClient`.
37 |
38 | Finally authorize your device via the `GoogleAuthenticatorClient` by passing the proper OAuth client.
39 |
40 | See the example project (using [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift) to see how it works in details.
41 |
42 | # Support for Limited Input Device Applications
43 |
44 | GAuth cares to support all platforms therefore it also implements the [Device Authentication Flow](https://tools.ietf.org/html/draft-ietf-oauth-v2-01#section-3.5.3) for devices that can not support the standard authorization flows (i.e. tvOS which does not include WebKit framework).
45 |
46 | # Roadmap
47 |
48 | - [x] Include code example
49 | - [ ] Add more Google Service Scope.
50 | - [ ] Add Travis CI/Circle CI to the repo
51 | - [ ] Add support for Swift 3
52 |
53 | # Credits
54 |
55 | GAuth's primary author is Fabio Milano, iOS Engineer at [Touchwonders](http://www.touchwonders.com).
56 |
57 | GAuth was inspired from the need of having a simple framework that could support all type of platforms and easily integrate with an existing OAuth client.
58 |
59 | # License
60 |
61 | GAuth is available under the MIT license. See the LICENSE file for more info.
--------------------------------------------------------------------------------
/Sources/G-Auth.h:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAuthenticator-iOS.h
3 | // GoogleAuthenticator-iOS
4 | //
5 | // Created by Fabio Milano on 28/03/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for GoogleAuthenticator-iOS.
12 | FOUNDATION_EXPORT double GAuthVersionNumber;
13 |
14 | //! Project version string for GoogleAuthenticator-iOS.
15 | FOUNDATION_EXPORT const unsigned char GAuthVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Sources/GoogleAuthenticatorClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAuthenticator.swift
3 | // GoogleAnalyticsReader
4 | //
5 | // Created by Fabio Milano on 05/02/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Result
11 |
12 | /// The Google Authenticator Client is responsible to provide an easy integration with Google authentication process for you.
13 | public final class GoogleAuthenticatorClient {
14 |
15 | /// The client ID used to initialize the authenticator
16 | public var clientID: String {
17 | return oauthClient.clientID
18 | }
19 |
20 | /// The client secret used to initialize the authenticator
21 | public var clientSecret: String {
22 | return oauthClient.clientSecret
23 | }
24 |
25 | /// The currently available token
26 | public var token: AuthorizedToken? {
27 | get {
28 | return oauthClient.token
29 | }
30 | }
31 |
32 | public var scope: GoogleServiceScope {
33 | return GoogleServiceScope(scopes: oauthClient.scopes)
34 | }
35 |
36 | /// The OAuth Client used to support the OAuth 2.0 specifications and requirements.
37 | internal let oauthClient: T
38 |
39 | /**
40 | Convenience initializer
41 |
42 | - parameter oauthClient: The OAuth Client inistantiation to use to complete the initialization.
43 |
44 | - returns: A fully equipped Google Authenticator Client ready to use.
45 | */
46 | public required init(oauthClient: T) {
47 | self.oauthClient = oauthClient
48 | }
49 |
50 | /**
51 | The designated initializer.
52 |
53 | - parameter consumerKey: The consumer key.
54 | - parameter consumerSecret: The consumer secret.
55 | - parameter bundleIdentifier: The bundler identifier used to register you app on Goolge Developer console.
56 | - parameter scope: The Google Scopes required to initiailize the authentication with Google Services.
57 |
58 | - returns: A fully equipped Google Authenticator Client ready for action.
59 | */
60 | public convenience init(consumerKey: String, consumerSecret: String, bundleIdentifier: String, scope:GoogleServiceScope) {
61 | let oauthClient = T.Google(clientID: consumerKey, clientSecret: consumerSecret, bundleIdentifier: bundleIdentifier, scope: scope) as! T
62 | self.init(oauthClient: oauthClient)
63 | }
64 |
65 | // MARK: Public methods
66 |
67 | public func authorizeDevice(verify:(verificationUrl: NSURL, userCode: String) -> Void, completion: Result -> Void) {
68 | let params = parametersForDeviceVerification()
69 |
70 | HTTP.POST(AuthenticationConstants.DeviceVerificationUrl.URL, parameters: params) { [weak self] resultJSON in
71 | guard let _ = self else {
72 | return
73 | }
74 |
75 | switch resultJSON {
76 | case .Success(let json):
77 | guard let deviceAuthorizationToken = DeviceAuthorizationToken(json: json) else {
78 | completion(.Failure(GoogleAuthenticatorError.InvalidAccessToken))
79 | return
80 | }
81 |
82 | dispatch_async(dispatch_get_main_queue(), {
83 | verify(verificationUrl: deviceAuthorizationToken.verificationUrl, userCode: deviceAuthorizationToken.userCode)
84 | })
85 |
86 | self!.pollAccessToken(deviceAuthorizationToken.deviceCode, retryInterval:deviceAuthorizationToken.retryInterval, completion:{ (result) in
87 | switch result {
88 | case .Success():
89 | completion(.Success())
90 | case .Failure(let error):
91 | completion(.Failure(error))
92 | }
93 | })
94 | case .Failure(let error):
95 | if let description = error.userInfo[NSLocalizedDescriptionKey] as? String {
96 | completion(.Failure(GoogleAuthenticatorError.DeviceVerificationFailed(description)))
97 | }
98 | }
99 | }
100 | }
101 |
102 | #if os(iOS)
103 | /**
104 | Every Google Authenticator needs to be authorized in order to make signed request.
105 | If no available tokens have been stored before, the hostViewController is used to present a standard WebView which guides the user to insert his credentials in order to kickoff the OAuth 2 dance to finally retrieve the required tokens.
106 | Otherwise, the authorization is skipped and available tokens are used to sign the requests.
107 | - parameter hostViewController: The host view controller used to present the credentials input.
108 | */
109 | public func authorize(completion: Result -> Void) {
110 |
111 | oauthClient.googleAuthenticatorAuthorize { (result) in
112 | switch result {
113 | case .Success( _):
114 | completion(.Success())
115 | case .Failure(let error):
116 | completion(.Failure(error.googleAuthenticatorError))
117 | }
118 | }
119 | }
120 | #endif
121 |
122 | // MARK: client methods
123 |
124 | /**
125 | - returns: `true` if current authenticator is already authorized (access_token and refresh_token available). `false` otherwise.
126 | */
127 | public func isAuthorized() -> Bool {
128 | guard let token = oauthClient.token else {
129 | return false
130 | }
131 |
132 | return !token.isExpired && !token.accessToken.isEmpty
133 | }
134 |
135 | /// Alters the given request by adding authentication, if possible.
136 | ///
137 | /// In case of an expired access token and the presence of a refresh token,
138 | /// automatically tries to refresh the access token. If refreshing the
139 | /// access token fails, the access token is cleared.
140 | ///
141 | /// **Note:** If the access token must be refreshed, network I/O is
142 | /// performed.
143 | ///
144 | /// **Note:** The completion closure may be invoked on any thread.
145 | ///
146 | /// - parameter request: An unauthenticated NSURLRequest.
147 | /// - parameter completion: A callback to invoke with the authenticated request.
148 | public func authenticateRequest(request: NSURLRequest, completion: Result -> ()) {
149 | if let token = token {
150 | if token.isExpired {
151 | // Expired token. Let's refresh it.
152 | oauthClient.googleAuthenticatorRefreshToken({ [weak self] result in
153 | switch result {
154 | case .Success( _):
155 | self?.authenticateRequest(request, completion: completion)
156 | case .Failure(let error):
157 | completion(Result.Failure(error.googleAuthenticatorError))
158 | }
159 | })
160 | } else {
161 | let mutableRequest = request.mutableCopy() as! NSMutableURLRequest
162 | mutableRequest.setAccessToken(token.accessToken)
163 | completion(Result.Success(mutableRequest))
164 | }
165 | } else {
166 | // No token available. The client must start an authentication process properly through -authorize method
167 | completion(Result.Failure(GoogleAuthenticatorError.InvalidAccessToken))
168 | }
169 | }
170 | }
171 |
172 | extension GoogleAuthenticatorClient {
173 | func parametersForDeviceVerification() -> [String: String] {
174 | return ["client_id": clientID, "scope": scope.string() ]
175 | }
176 |
177 | func parametersForDeviceAuthorization(userCode: String) -> [String: String] {
178 | return ["client_id": clientID, "client_secret": clientSecret, "code": userCode, "grant_type": "http://oauth.net/grant_type/device/1.0"]
179 | }
180 |
181 | func pollAccessToken(deviceCode: String, retryInterval: NSTimeInterval, completion: Result -> Void) -> Void {
182 | oauthClient.googleAuthenticatorAuthorizeDeviceCode(deviceCode, completion: { (result) in
183 | switch result {
184 | case .Success( _):
185 | completion(.Success())
186 | case .Failure (let error):
187 |
188 | switch error.googleAuthenticatorError {
189 | case .AuthorizationPending:
190 | delay(retryInterval, closure: { [weak self] in
191 | guard let _ = self else {
192 | return
193 | }
194 |
195 | self!.pollAccessToken(deviceCode, retryInterval: retryInterval, completion: completion)
196 | })
197 | default:
198 | completion(.Failure(error.googleAuthenticatorError))
199 | }
200 | }
201 | })
202 | }
203 | }
204 |
205 | /** List of all available Google scopes supported by the Google Authenticator
206 | Get full scopes list at: https://developers.google.com/identity/protocols/googlescopes
207 |
208 | - GoogleAnalyticsRead: View your Google Analytics data.
209 | - Custom(String): Define a custom scope.
210 | - Collection([String]): Define a collection of scopes as an array of `String`
211 | */
212 | public enum GoogleServiceScope {
213 | case GoogleAnalyticsRead
214 | case Custom(String)
215 | case Collection([String])
216 |
217 | public func string() -> String {
218 | switch self {
219 | case .GoogleAnalyticsRead:
220 | return "https://www.googleapis.com/auth/analytics.readonly"
221 | case Custom(let scopeString):
222 | return scopeString
223 | case Collection(let scopeCollection):
224 | return scopeCollection.joinWithSeparator(" ")
225 | }
226 | }
227 |
228 | init(scopeString: String) {
229 | switch scopeString {
230 | case "https://www.googleapis.com/auth/analytics.readonly":
231 | self = .GoogleAnalyticsRead
232 | default:
233 | self = .Custom(scopeString)
234 | }
235 | }
236 |
237 | init(scopes: [String]) {
238 | self = .Collection(scopes)
239 | }
240 |
241 | public func isEqualToScope(scope: GoogleServiceScope) -> Bool {
242 | return self.string() == scope.string()
243 | }
244 | }
--------------------------------------------------------------------------------
/Sources/Info-iOS.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.2.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Sources/Info-tvOS.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.2.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Sources/Info-watchOS.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.2.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Sources/Models/DeviceAuthorizationToken.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserCode.swift
3 | // GoogleAuthenticator
4 | //
5 | // Created by Fabio Milano on 12/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // EXAMPLE
12 | //{
13 | // "device_code" : "THE DEVICE CODE",
14 | // "user_code" : "THE USER CODE",
15 | // "verification_url" : "https://www.google.com/device",
16 | // "expires_in" : 1800,
17 | // "interval" : 5
18 | //}
19 |
20 | /**
21 | * A DeviceAuthorizationToken struct represents the response object when starting an authentication via device token.
22 | */
23 | internal struct DeviceAuthorizationToken {
24 |
25 | /// The device code related to the authorization request.
26 | let deviceCode: String
27 |
28 | /// The user code. This should be shown to the user to complete the authorization process.
29 | let userCode: String
30 |
31 | /// The verification URL to which the user should connect to in order to complete the authorization process.
32 | let verificationUrl: NSURL
33 |
34 | /// Expiration of the user code expressend in NSTimeInterval
35 | let expiresIn: NSTimeInterval
36 |
37 | /// The retry interval to use when polling the authorization against Google
38 | let retryInterval: NSTimeInterval
39 |
40 | init?(json: [String: AnyObject]) {
41 | guard let deviceCode = json[UserCodeJSONKey.DeviceCode.rawValue] as? String,
42 | verificationUrl = json[UserCodeJSONKey.VerificationUrl.rawValue] as? String,
43 | userCode = json[UserCodeJSONKey.UserCode.rawValue] as? String,
44 | expiresIn = json[UserCodeJSONKey.ExpiresIn.rawValue] as? NSTimeInterval,
45 | retryInterval = json[UserCodeJSONKey.RetryInterval.rawValue] as? NSTimeInterval else {
46 | return nil
47 | }
48 |
49 | self.deviceCode = deviceCode
50 | self.userCode = userCode
51 | self.verificationUrl = NSURL(string: verificationUrl)!
52 | self.expiresIn = expiresIn
53 | self.retryInterval = retryInterval
54 | }
55 | }
56 |
57 | private enum UserCodeJSONKey: String {
58 | case DeviceCode = "device_code"
59 | case VerificationUrl = "verification_url"
60 | case UserCode = "user_code"
61 | case ExpiresIn = "expires_in"
62 | case RetryInterval = "interval"
63 | }
--------------------------------------------------------------------------------
/Sources/Models/GoogleAuthenticatorError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAuthenticatorError.swift
3 | // GoogleAuthenticator
4 | //
5 | // Created by Fabio Milano on 11/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | * The designated representation for an error
13 | */
14 | public enum GoogleAuthenticatorError: ErrorType {
15 | /// Error caused during the authorization flow
16 | case AuthorizationError(String)
17 |
18 | /// The current token is invalid. The user should rissue another autorization process.
19 | case InvalidAccessToken
20 |
21 | /// The authorization process is pending user input.
22 | case AuthorizationPending
23 |
24 | /// The device verification flow failed. Retry is adviced.
25 | case DeviceVerificationFailed(String)
26 | }
--------------------------------------------------------------------------------
/Sources/Models/OAuthIntegrationProtocols.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OAuthIntegrationProtocol.swift
3 | // GoogleAuthenticator
4 | //
5 | // Created by Fabio Milano on 13/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 |
10 | import Foundation
11 | import Result
12 |
13 | /**
14 | * Defines the requirements for any type that represents an Access Token, such that it can be used by the `GoogleAuthenticatorClient`
15 | */
16 | public protocol AuthorizedToken {
17 |
18 | /// The access token.
19 | var accessToken: String { get }
20 |
21 | /// The refresh token.
22 | var refreshToken: String? { get }
23 |
24 | /// The expiration time for current access token in seconds.
25 | var expiresIn: NSTimeInterval? { get }
26 |
27 | /// Convenience getter to know if current token has expired.
28 | var isExpired: Bool { get }
29 | }
30 |
31 | /**
32 | * Defines the requirements for an error type to be processable by the `GoogleAuthenticatorClient`
33 | */
34 | public protocol GoogleAuthenticatorErrorAdapter: ErrorType {
35 | /// The representation of the error as GoogleAuthenticatorError type.
36 | var googleAuthenticatorError: GoogleAuthenticatorError { get }
37 | }
38 |
39 | /**
40 | * Defines the requirements for an OAuth client type to be used to initialize a `GoogleAuthenticatorClient`
41 | */
42 | public protocol GoogleAuthenticatorOAuthClient {
43 | /// The type for the OAuth Client that the authenticator requires to handle OAuth requests and specifications
44 | associatedtype OAuthClientType
45 |
46 | /// The type used to represent a token retrieved via the OAuth Client
47 | associatedtype TokenType: AuthorizedToken
48 |
49 | /// The failure type that is used by the OAuth Client. Additionally, this type, must conform to the protocol needed to evaluate the error in the Google Authenticator implementation.
50 | associatedtype Failure: GoogleAuthenticatorErrorAdapter
51 |
52 | /// The client ID.
53 | var clientID: String { get }
54 |
55 | /// The client secret.
56 | var clientSecret: String { get }
57 |
58 | /// The authorize URL.
59 | var authorizeURL: NSURL { get }
60 |
61 | /// The token URL.
62 | var tokenURL: NSURL? { get }
63 |
64 | /// The redirect URL.
65 | var redirectURL: NSURL? { get }
66 |
67 | /// The token structure used by the OAuth client
68 | var token: TokenType? { get }
69 |
70 | /// The requested scopes.
71 | var scopes: [String] { get }
72 |
73 | /**
74 | The designated initializer for the OAuth Client encapsulate by the Google Authenticator Client
75 |
76 | - Parameters:
77 | - clientID: a.k.a. The consumer id
78 | - clientSecret: a.k.a. The consumer secret
79 | - bundleIdentifier: The bundle identifier of the app is used to build the proper callback via deep linking to the app
80 | - scope: The scope to authorize. Complete list available at `https://developers.google.com/identity/protocols/googlescopes`
81 | */
82 | static func Google(clientID clientID: String, clientSecret: String, bundleIdentifier: String, scope: GoogleServiceScope) -> OAuthClientType
83 |
84 | #if os(iOS)
85 | /**
86 | Request access token.
87 | This method is called by the authenticator to kick off the OAuth 2.0 authentication process with its OAuthClient.
88 |
89 | - Parameters:
90 | - completion: The completion block. Takes a Result as parameter.
91 | - Success: Takes just issued access token as value
92 | - Failure: The encountered failure.
93 | */
94 | func googleAuthenticatorAuthorize(completion: Result -> Void)
95 | #endif
96 |
97 | /**
98 | Refresh the available access token.
99 | This method is called by the authenticator when the access token provided by the OAuth client has expired and requires to be refreshed.
100 |
101 | - Parameters:
102 | - completion: The completion block. Takes a `Result` as parameter.
103 | - Success: Takes the a `refreshed` access token as value
104 | - Failure: The encountered failure.
105 | */
106 | func googleAuthenticatorRefreshToken(completion: Result -> Void)
107 |
108 | /**
109 | Request an access token via device code. For more details see `https://developers.google.com/identity/protocols/OAuth2ForDevices`
110 | This method is called by the authenticator when the access token is requested from device with limited capabilities.
111 |
112 | - Parameters:
113 | - completion: The completion block. Takes a `Result` as parameter.
114 | - Success: Takes the access token as value
115 | - Failure: The encountered failure.
116 | */
117 | func googleAuthenticatorAuthorizeDeviceCode(deviceCode: String, completion: Result -> Void)
118 | }
119 |
120 | /**
121 | Struct constant required to handle the authentication process with Google
122 |
123 | - AuthorizeUrl: The authorization URL used when requesting the authorization code (used to retrieve the oauth tokens)
124 | - AccessTokensUrl: The access token URL used to retrieve the OAuth tokens: access_token and refresh_token
125 | - CallbackPostfix: The string to append to the bundle identiifier to compose the callback URL used in the OAuth 2.0 authentication process.
126 | - DeviceVerificationUrl: The endpoint to use to kickoff the OAuth 2.0 authorization via devices with limited capabilities.
127 | */
128 | public enum AuthenticationConstants: String {
129 | case AuthorizeUrl = "https://accounts.google.com/o/oauth2/auth"
130 | case AccessTokensUrl = "https://www.googleapis.com/oauth2/v4/token"
131 | case CallbackPostfix = ":/urn:ietf:wg:oauth:2.0:oob"
132 | case DeviceVerificationUrl = "https://accounts.google.com/o/oauth2/device/code"
133 |
134 | /// A convenience getter to retrienve the URL representation for every enum value.
135 | public var URL: NSURL {
136 | return NSURL(string: self.rawValue)!
137 | }
138 | }
--------------------------------------------------------------------------------
/Sources/Utilities/DispatchAfter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DispatchAfter.swift
3 | //
4 | // Created by Nikita Took on 08/04/15.
5 | // Copyright (c) 2015. All rights reserved.
6 | //
7 | // inspired by implementation of Matt Neuburg (http://stackoverflow.com/users/341994/matt)
8 | //
9 |
10 | import Foundation
11 |
12 | public func delay(aDelay:NSTimeInterval, closure: () -> Void) {
13 |
14 | delay(aDelay, queue: dispatch_get_main_queue(), closure: closure)
15 |
16 | }
17 |
18 | public func delay(aDelay:NSTimeInterval, queue: dispatch_queue_t!, closure: () -> Void) {
19 |
20 | let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(aDelay * Double(NSEC_PER_SEC)))
21 | dispatch_after(delayTime, queue, closure)
22 |
23 | }
24 |
25 | public extension Int {
26 | var second: NSTimeInterval { return NSTimeInterval(self) }
27 | var seconds: NSTimeInterval { return NSTimeInterval(self) }
28 | var minute: NSTimeInterval { return NSTimeInterval(self * 60) }
29 | var minutes: NSTimeInterval { return NSTimeInterval(self * 60) }
30 | var hour: NSTimeInterval { return NSTimeInterval(self * 3600) }
31 | var hours: NSTimeInterval { return NSTimeInterval(self * 3600) }
32 | }
33 |
34 | public extension Double {
35 | var second: NSTimeInterval { return self }
36 | var seconds: NSTimeInterval { return self }
37 | var minute: NSTimeInterval { return self * 60 }
38 | var minutes: NSTimeInterval { return self * 60 }
39 | var hour: NSTimeInterval { return self * 3600 }
40 | var hours: NSTimeInterval { return self * 3600 }
41 | }
--------------------------------------------------------------------------------
/Sources/Utilities/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // GoogleAuthenticator
4 | //
5 | // Created by Fabio Milano on 12/06/16.
6 | // Copyright © 2016 Touchwonders. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSMutableURLRequest {
12 | public func setAccessToken(accessToken: String) -> Void {
13 | self.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
14 | }
15 | }
--------------------------------------------------------------------------------
/Sources/Utilities/HTTPClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTP.swift
3 | //
4 | // Copyright (c) 2016 Damien (http://delba.io)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | import Foundation
26 | import Result
27 |
28 | typealias JSON = [String: AnyObject]
29 |
30 | public let HTTPErrorDomain = "HTTPErrorDomain"
31 |
32 | internal enum HTTPErrorCode: Int {
33 | case NoDataFound = 1
34 | case JSONDeserializationError = 2
35 | }
36 |
37 | internal struct HTTP {
38 | static func POST(URL: NSURL, parameters: [String: String], completion: Result -> Void) {
39 | let request = NSMutableURLRequest(URL: URL)
40 |
41 | request.setValue("application/json", forHTTPHeaderField: "Accept")
42 |
43 | request.HTTPMethod = "POST"
44 |
45 | request.HTTPBody = parameters.map { "\($0)=\($1)" }
46 | .joinWithSeparator("&")
47 | .dataUsingEncoding(NSUTF8StringEncoding)
48 |
49 | let session = NSURLSession.sharedSession()
50 |
51 | let task = session.dataTaskWithRequest(request) { data, response, error in
52 | if let error = error {
53 | completion(.Failure(error))
54 | return
55 | }
56 |
57 | guard let data = data else {
58 | let userInfo = [ NSLocalizedDescriptionKey : "No data found" ]
59 | let error = NSError(domain: HTTPErrorDomain, code: HTTPErrorCode.NoDataFound.rawValue, userInfo: userInfo)
60 | completion(.Failure(error))
61 | return
62 | }
63 |
64 | do {
65 | let object = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
66 | if let dictionary = object as? JSON {
67 | completion(.Success(dictionary))
68 | } else {
69 | let userInfo = [ NSLocalizedDescriptionKey : "Cannot parse response" ]
70 | let error = NSError(domain: HTTPErrorDomain, code: HTTPErrorCode.JSONDeserializationError.rawValue, userInfo: userInfo)
71 | completion(.Failure(error))
72 | }
73 | } catch {
74 | completion(.Failure(error as NSError))
75 | }
76 | }
77 |
78 | task.resume()
79 | }
80 | }
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | CARTHAGE := $(shell command -v carthage)
2 |
3 | setup:
4 | ifeq ("$(CARTHAGE)","")
5 | $(error ${\n}Carthage is not installed.${\n}See https://github.com/Carthage/Carthage for install instructions)
6 | endif
7 | carthage bootstrap --platform iOS --no-use-binaries
8 |
9 | define \n
10 |
11 |
12 | endef
--------------------------------------------------------------------------------