├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── Deps └── README.md ├── Examples ├── OAuth2Sample │ ├── Info.plist │ ├── MainMenu.xib │ ├── OAuth2Sample.xcodeproj │ │ └── project.pbxproj │ ├── OAuth2Sample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── xcschemes │ │ │ └── OAuth2Sample.xcscheme │ ├── OAuth2SampleAppController.h │ ├── OAuth2SampleAppController.m │ ├── OAuth2Sample_Prefix.pch │ └── main.m └── OAuth2SampleTouch │ ├── Info.plist │ ├── OAuth2SampleAppDelegateTouch.h │ ├── OAuth2SampleAppDelegateTouch.m │ ├── OAuth2SampleMainTouch.xib │ ├── OAuth2SampleMainiPad.xib │ ├── OAuth2SampleRootViewControllerTouch.h │ ├── OAuth2SampleRootViewControllerTouch.m │ ├── OAuth2SampleTouch.xcodeproj │ └── project.pbxproj │ ├── OAuth2SampleTouch.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── xcschemes │ │ └── OAuthSampleTouch.xcscheme │ ├── README.txt │ ├── mainTouch.m │ └── prefixTouch.pch ├── GTMOAuth2.podspec ├── LICENSE ├── README.md ├── ReleaseNotes.md └── Source ├── GTMOAuth2.xcodeproj └── project.pbxproj ├── GTMOAuth2Authentication.h ├── GTMOAuth2Authentication.m ├── GTMOAuth2SignIn.h ├── GTMOAuth2SignIn.m ├── Mac ├── GTMOAuth2Framework-Info.plist ├── GTMOAuth2Window.xib ├── GTMOAuth2WindowController.h └── GTMOAuth2WindowController.m └── Touch ├── GTMOAuth2ViewControllerTouch.h ├── GTMOAuth2ViewControllerTouch.m └── GTMOAuth2ViewTouch.xib /.gitignore: -------------------------------------------------------------------------------- 1 | # Any place they exist. 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Deps/gtm-session-fetcher"] 2 | path = Deps/gtm-session-fetcher 3 | url = https://github.com/google/gtm-session-fetcher.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute # 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | a just a few small guidelines you need to follow. 5 | 6 | 7 | ## Contributor License Agreement ## 8 | 9 | Contributions to any Google project must be accompanied by a Contributor 10 | License Agreement. This is not a copyright **assignment**, it simply gives 11 | Google permission to use and redistribute your contributions as part of the 12 | project. 13 | 14 | * If you are an individual writing original source code and you're sure you 15 | own the intellectual property, then you'll need to sign an [individual 16 | CLA][]. 17 | 18 | * If you work for a company that wants to allow you to contribute your work, 19 | then you'll need to sign a [corporate CLA][]. 20 | 21 | You generally only need to submit a CLA once, so if you've already submitted 22 | one (even if it was for a different project), you probably don't need to do it 23 | again. 24 | 25 | [individual CLA]: https://developers.google.com/open-source/cla/individual 26 | [corporate CLA]: https://developers.google.com/open-source/cla/corporate 27 | 28 | 29 | ## Submitting a patch ## 30 | 31 | 1. It's generally best to start by opening a new issue describing the bug or 32 | feature you're intending to fix. Even if you think it's relatively minor, 33 | it's helpful to know what people are working on. Mention in the initial 34 | issue that you are planning to work on that bug or feature so that it can 35 | be assigned to you. 36 | 37 | 1. Follow the normal process of [forking][] the project, and setup a new 38 | branch to work in. It's important that each group of changes be done in 39 | separate branches in order to ensure that a pull request only includes the 40 | commits related to that bug or feature. 41 | 42 | 1. Any significant changes should almost always be accompanied by tests. The 43 | project already has good test coverage, so look at some of the existing 44 | tests if you're unsure how to go about it. 45 | 46 | 1. Do your best to have [well-formed commit messages][] for each change. 47 | This provides consistency throughout the project, and ensures that commit 48 | messages are able to be formatted properly by various git tools. 49 | 50 | 1. Finally, push the commits to your fork and submit a [pull request][]. 51 | 52 | [forking]: https://help.github.com/articles/fork-a-repo 53 | [well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 54 | [pull request]: https://help.github.com/articles/creating-a-pull-request 55 | -------------------------------------------------------------------------------- /Deps/README.md: -------------------------------------------------------------------------------- 1 | This directory contains git submodules for the other projects that this project 2 | depends on. 3 | 4 | It is mainly here for the samples. Using this module via CocoaPods will directly 5 | pull in the dependencies. 6 | 7 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.example.${PRODUCT_NAME:identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | NSMainNibFile 24 | MainMenu 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | 29 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/OAuth2Sample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4F2446C5121F1F5B00FEE1DA /* GTMOAuth2Authentication.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F2446BF121F1F5B00FEE1DA /* GTMOAuth2Authentication.m */; }; 11 | 4F2446C6121F1F5B00FEE1DA /* GTMOAuth2SignIn.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F2446C1121F1F5B00FEE1DA /* GTMOAuth2SignIn.m */; }; 12 | 4F24471B121F205800FEE1DA /* GTMOAuth2Window.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F244718121F205800FEE1DA /* GTMOAuth2Window.xib */; }; 13 | 4F24471C121F205800FEE1DA /* GTMOAuth2WindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F24471A121F205800FEE1DA /* GTMOAuth2WindowController.m */; }; 14 | 4F244A4D121F5CA400FEE1DA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F244A4C121F5CA400FEE1DA /* MainMenu.xib */; }; 15 | 4F78829712F795CC001EE2A6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F78829612F795CC001EE2A6 /* main.m */; }; 16 | 4FDE34C71132165E00700DC8 /* OAuth2SampleAppController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FDE34C61132165E00700DC8 /* OAuth2SampleAppController.m */; }; 17 | 4FDE353C1132197800700DC8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FDE353B1132197800700DC8 /* Security.framework */; }; 18 | 4FDE359D11321EAA00700DC8 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FDE359C11321EAA00700DC8 /* WebKit.framework */; }; 19 | 4FEE14211BB2204F002ACBF8 /* GTMSessionFetcherOSX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FEE14201BB2204F002ACBF8 /* GTMSessionFetcherOSX.framework */; }; 20 | 4FF23D5911753FA800E96C5A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FF23D5811753FA800E96C5A /* SystemConfiguration.framework */; }; 21 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 26 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 27 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 28 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 29 | 4F2446BE121F1F5B00FEE1DA /* GTMOAuth2Authentication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMOAuth2Authentication.h; path = ../../Source/GTMOAuth2Authentication.h; sourceTree = SOURCE_ROOT; }; 30 | 4F2446BF121F1F5B00FEE1DA /* GTMOAuth2Authentication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMOAuth2Authentication.m; path = ../../Source/GTMOAuth2Authentication.m; sourceTree = SOURCE_ROOT; }; 31 | 4F2446C0121F1F5B00FEE1DA /* GTMOAuth2SignIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMOAuth2SignIn.h; path = ../../Source/GTMOAuth2SignIn.h; sourceTree = SOURCE_ROOT; }; 32 | 4F2446C1121F1F5B00FEE1DA /* GTMOAuth2SignIn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMOAuth2SignIn.m; path = ../../Source/GTMOAuth2SignIn.m; sourceTree = SOURCE_ROOT; }; 33 | 4F244718121F205800FEE1DA /* GTMOAuth2Window.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = GTMOAuth2Window.xib; path = ../../Source/Mac/GTMOAuth2Window.xib; sourceTree = SOURCE_ROOT; }; 34 | 4F244719121F205800FEE1DA /* GTMOAuth2WindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMOAuth2WindowController.h; path = ../../Source/Mac/GTMOAuth2WindowController.h; sourceTree = SOURCE_ROOT; }; 35 | 4F24471A121F205800FEE1DA /* GTMOAuth2WindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMOAuth2WindowController.m; path = ../../Source/Mac/GTMOAuth2WindowController.m; sourceTree = SOURCE_ROOT; }; 36 | 4F244A4C121F5CA400FEE1DA /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 37 | 4F78829612F795CC001EE2A6 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 38 | 4FDE34C51132165E00700DC8 /* OAuth2SampleAppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAuth2SampleAppController.h; sourceTree = ""; }; 39 | 4FDE34C61132165E00700DC8 /* OAuth2SampleAppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAuth2SampleAppController.m; sourceTree = ""; }; 40 | 4FDE353B1132197800700DC8 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; 41 | 4FDE359C11321EAA00700DC8 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = /System/Library/Frameworks/WebKit.framework; sourceTree = ""; }; 42 | 4FEE14201BB2204F002ACBF8 /* GTMSessionFetcherOSX.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GTMSessionFetcherOSX.framework; path = ../../Deps/gtm-session-fetcher/Source/build/Debug/GTMSessionFetcherOSX.framework; sourceTree = ""; }; 43 | 4FF23D5811753FA800E96C5A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; 44 | 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 8D1107320486CEB800E47090 /* OAuth2Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OAuth2Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 8D11072E0486CEB800E47090 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | 4FEE14211BB2204F002ACBF8 /* GTMSessionFetcherOSX.framework in Frameworks */, 54 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 55 | 4FDE353C1132197800700DC8 /* Security.framework in Frameworks */, 56 | 4FDE359D11321EAA00700DC8 /* WebKit.framework in Frameworks */, 57 | 4FF23D5911753FA800E96C5A /* SystemConfiguration.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 080E96DDFE201D6D7F000001 /* Sample App Sources */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 4FDE34C51132165E00700DC8 /* OAuth2SampleAppController.h */, 68 | 4FDE34C61132165E00700DC8 /* OAuth2SampleAppController.m */, 69 | 8D1107310486CEB800E47090 /* Info.plist */, 70 | 4F244A4C121F5CA400FEE1DA /* MainMenu.xib */, 71 | 4F78829612F795CC001EE2A6 /* main.m */, 72 | ); 73 | name = "Sample App Sources"; 74 | sourceTree = ""; 75 | }; 76 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 4FF23D5811753FA800E96C5A /* SystemConfiguration.framework */, 80 | 4FDE359C11321EAA00700DC8 /* WebKit.framework */, 81 | 4FDE353B1132197800700DC8 /* Security.framework */, 82 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 83 | ); 84 | name = "Linked Frameworks"; 85 | sourceTree = ""; 86 | }; 87 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 91 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, 92 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */, 93 | ); 94 | name = "Other Frameworks"; 95 | sourceTree = ""; 96 | }; 97 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 8D1107320486CEB800E47090 /* OAuth2Sample.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 29B97314FDCFA39411CA2CEA /* OAuthSample */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 080E96DDFE201D6D7F000001 /* Sample App Sources */, 109 | 4FDE350F1132188300700DC8 /* OAuth Sources */, 110 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 111 | 19C28FACFE9D520D11CA2CBB /* Products */, 112 | ); 113 | name = OAuthSample; 114 | sourceTree = ""; 115 | }; 116 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 4FEE14201BB2204F002ACBF8 /* GTMSessionFetcherOSX.framework */, 120 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 121 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, 122 | ); 123 | name = Frameworks; 124 | sourceTree = ""; 125 | }; 126 | 4FDE350F1132188300700DC8 /* OAuth Sources */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 4F244719121F205800FEE1DA /* GTMOAuth2WindowController.h */, 130 | 4F24471A121F205800FEE1DA /* GTMOAuth2WindowController.m */, 131 | 4F2446BE121F1F5B00FEE1DA /* GTMOAuth2Authentication.h */, 132 | 4F2446BF121F1F5B00FEE1DA /* GTMOAuth2Authentication.m */, 133 | 4F2446C0121F1F5B00FEE1DA /* GTMOAuth2SignIn.h */, 134 | 4F2446C1121F1F5B00FEE1DA /* GTMOAuth2SignIn.m */, 135 | 4F244718121F205800FEE1DA /* GTMOAuth2Window.xib */, 136 | ); 137 | name = "OAuth Sources"; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 8D1107260486CEB800E47090 /* OAuth2Sample */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "OAuth2Sample" */; 146 | buildPhases = ( 147 | 8D1107290486CEB800E47090 /* Resources */, 148 | 8D11072C0486CEB800E47090 /* Sources */, 149 | 8D11072E0486CEB800E47090 /* Frameworks */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = OAuth2Sample; 156 | productInstallPath = "$(HOME)/Applications"; 157 | productName = OAuthSample; 158 | productReference = 8D1107320486CEB800E47090 /* OAuth2Sample.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | /* End PBXNativeTarget section */ 162 | 163 | /* Begin PBXProject section */ 164 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 165 | isa = PBXProject; 166 | attributes = { 167 | LastUpgradeCheck = 0730; 168 | }; 169 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "OAuth2Sample" */; 170 | compatibilityVersion = "Xcode 3.2"; 171 | developmentRegion = English; 172 | hasScannedForEncodings = 1; 173 | knownRegions = ( 174 | English, 175 | Japanese, 176 | French, 177 | German, 178 | ); 179 | mainGroup = 29B97314FDCFA39411CA2CEA /* OAuthSample */; 180 | projectDirPath = ""; 181 | projectRoot = ""; 182 | targets = ( 183 | 8D1107260486CEB800E47090 /* OAuth2Sample */, 184 | ); 185 | }; 186 | /* End PBXProject section */ 187 | 188 | /* Begin PBXResourcesBuildPhase section */ 189 | 8D1107290486CEB800E47090 /* Resources */ = { 190 | isa = PBXResourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 4F24471B121F205800FEE1DA /* GTMOAuth2Window.xib in Resources */, 194 | 4F244A4D121F5CA400FEE1DA /* MainMenu.xib in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXSourcesBuildPhase section */ 201 | 8D11072C0486CEB800E47090 /* Sources */ = { 202 | isa = PBXSourcesBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | 4FDE34C71132165E00700DC8 /* OAuth2SampleAppController.m in Sources */, 206 | 4F2446C5121F1F5B00FEE1DA /* GTMOAuth2Authentication.m in Sources */, 207 | 4F2446C6121F1F5B00FEE1DA /* GTMOAuth2SignIn.m in Sources */, 208 | 4F24471C121F205800FEE1DA /* GTMOAuth2WindowController.m in Sources */, 209 | 4F78829712F795CC001EE2A6 /* main.m in Sources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXSourcesBuildPhase section */ 214 | 215 | /* Begin XCBuildConfiguration section */ 216 | C01FCF4B08A954540054247B /* Debug */ = { 217 | isa = XCBuildConfiguration; 218 | buildSettings = { 219 | ALWAYS_SEARCH_USER_PATHS = NO; 220 | COMBINE_HIDPI_IMAGES = YES; 221 | COPY_PHASE_STRIP = NO; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_MODEL_TUNING = G5; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 226 | GCC_PREFIX_HEADER = OAuth2Sample_Prefix.pch; 227 | GCC_PREPROCESSOR_DEFINITIONS = ( 228 | "GTM_OAUTH2_USE_FRAMEWORK_IMPORTS=1", 229 | "GTM_OAUTH2_USE_PLATFORM_FRAMEWORK=1", 230 | ); 231 | INFOPLIST_FILE = Info.plist; 232 | INSTALL_PATH = "$(HOME)/Applications"; 233 | MACOSX_DEPLOYMENT_TARGET = 10.10; 234 | PRODUCT_BUNDLE_IDENTIFIER = "com.example.${PRODUCT_NAME:identifier}"; 235 | PRODUCT_NAME = OAuth2Sample; 236 | }; 237 | name = Debug; 238 | }; 239 | C01FCF4C08A954540054247B /* Release */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | ALWAYS_SEARCH_USER_PATHS = NO; 243 | COMBINE_HIDPI_IMAGES = YES; 244 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 245 | GCC_MODEL_TUNING = G5; 246 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 247 | GCC_PREFIX_HEADER = OAuth2Sample_Prefix.pch; 248 | GCC_PREPROCESSOR_DEFINITIONS = ( 249 | "GTM_OAUTH2_USE_FRAMEWORK_IMPORTS=1", 250 | "GTM_OAUTH2_USE_PLATFORM_FRAMEWORK=1", 251 | ); 252 | INFOPLIST_FILE = Info.plist; 253 | INSTALL_PATH = "$(HOME)/Applications"; 254 | MACOSX_DEPLOYMENT_TARGET = 10.10; 255 | PRODUCT_BUNDLE_IDENTIFIER = "com.example.${PRODUCT_NAME:identifier}"; 256 | PRODUCT_NAME = OAuth2Sample; 257 | }; 258 | name = Release; 259 | }; 260 | C01FCF4F08A954540054247B /* Debug */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ENABLE_TESTABILITY = YES; 264 | GCC_C_LANGUAGE_STANDARD = c99; 265 | GCC_OPTIMIZATION_LEVEL = 0; 266 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 267 | GCC_WARN_UNUSED_VARIABLE = YES; 268 | ONLY_ACTIVE_ARCH = YES; 269 | OTHER_CFLAGS = "-DDEBUG=1"; 270 | RUN_CLANG_STATIC_ANALYZER = YES; 271 | SDKROOT = macosx; 272 | }; 273 | name = Debug; 274 | }; 275 | C01FCF5008A954540054247B /* Release */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | GCC_C_LANGUAGE_STANDARD = c99; 279 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 280 | GCC_WARN_UNUSED_VARIABLE = YES; 281 | SDKROOT = macosx; 282 | }; 283 | name = Release; 284 | }; 285 | /* End XCBuildConfiguration section */ 286 | 287 | /* Begin XCConfigurationList section */ 288 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "OAuth2Sample" */ = { 289 | isa = XCConfigurationList; 290 | buildConfigurations = ( 291 | C01FCF4B08A954540054247B /* Debug */, 292 | C01FCF4C08A954540054247B /* Release */, 293 | ); 294 | defaultConfigurationIsVisible = 0; 295 | defaultConfigurationName = Release; 296 | }; 297 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "OAuth2Sample" */ = { 298 | isa = XCConfigurationList; 299 | buildConfigurations = ( 300 | C01FCF4F08A954540054247B /* Debug */, 301 | C01FCF5008A954540054247B /* Release */, 302 | ); 303 | defaultConfigurationIsVisible = 0; 304 | defaultConfigurationName = Release; 305 | }; 306 | /* End XCConfigurationList section */ 307 | }; 308 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 309 | } 310 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/OAuth2Sample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/OAuth2Sample.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/OAuth2Sample.xcworkspace/xcshareddata/xcschemes/OAuth2Sample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/OAuth2SampleAppController.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #import 17 | 18 | #import "GTMOAuth2WindowController.h" 19 | 20 | @interface OAuth2SampleAppController : NSObject { 21 | @private 22 | IBOutlet NSWindow *mMainWindow; 23 | IBOutlet NSButton *mSignInOutButton; 24 | IBOutlet NSButton *mDoAnAuthenticatedFetchButton; 25 | IBOutlet NSButton *mPersistUserCheckbox; 26 | IBOutlet NSButton *mExpireNowButton; 27 | IBOutlet NSMatrix *mRadioButtons; 28 | IBOutlet NSTextField *mClientIDField; 29 | IBOutlet NSTextField *mClientSecretField; 30 | IBOutlet NSTextField *mUsernameField; 31 | IBOutlet NSTextField *mServiceNameField; 32 | IBOutlet NSTextField *mAccessTokenField; 33 | IBOutlet NSTextField *mExpirationField; 34 | IBOutlet NSTextField *mRefreshTokenField; 35 | IBOutlet NSProgressIndicator *mSpinner; 36 | IBOutlet NSTextView *mAPIResultTextView; 37 | 38 | GTMOAuth2Authentication *mAuth; 39 | 40 | NSUInteger mAuthFetchersRunningCount; 41 | } 42 | 43 | - (IBAction)signInOutClicked:(id)sender; 44 | 45 | - (IBAction)radioButtonClicked:(id)sender; 46 | 47 | - (IBAction)APIConsoleClicked:(id)sender; 48 | 49 | - (IBAction)expireNowClicked:(id)sender; 50 | 51 | - (IBAction)doAnAuthenticatedAPIFetchClicked:(id)sender; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/OAuth2SampleAppController.m: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #import "OAuth2SampleAppController.h" 17 | 18 | @interface OAuth2SampleAppController () 19 | - (void)signInToGoogle; 20 | - (void)signInToDailyMotion; 21 | 22 | - (void)signOut; 23 | - (BOOL)isSignedIn; 24 | 25 | - (void)doAnAuthenticatedAPIFetch; 26 | - (GTMOAuth2Authentication *)authForDailyMotion; 27 | 28 | - (void)windowController:(GTMOAuth2WindowController *)windowController 29 | finishedWithAuth:(GTMOAuth2Authentication *)auth 30 | error:(NSError *)error; 31 | - (void)updateUI; 32 | - (void)setAuthentication:(GTMOAuth2Authentication *)auth; 33 | - (void)signInFetchStateChanged:(NSNotification *)note; 34 | - (void)signInNetworkLost:(NSNotification *)note; 35 | 36 | - (void)saveClientIDValues; 37 | - (void)loadClientIDValues; 38 | @end 39 | 40 | @implementation OAuth2SampleAppController 41 | 42 | static NSString *const kKeychainItemName = @"OAuth2 Sample: Google Plus"; 43 | 44 | static NSString *const kDailyMotionKeychainItemName = @"OAuth2 Sample: DailyMotion"; 45 | 46 | static NSString *const kDailyMotionServiceName = @"DailyMotion"; 47 | 48 | // NSUserDefaults keys 49 | static NSString *const kGoogleClientIDKey = @"GoogleClientID"; 50 | static NSString *const kGoogleClientSecretKey = @"GoogleClientSecret"; 51 | static NSString *const kDailyMotionClientIDKey = @"DailyMotionClientID"; 52 | static NSString *const kDailyMotionClientSecretKey = @"DailyMotionClientSecret"; 53 | 54 | - (void)awakeFromNib { 55 | // Fill in the Client ID and Client Secret text fields 56 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 57 | 58 | // First, we'll try to get the saved Google authentication, if any, from 59 | // the keychain 60 | 61 | // Normal applications will hardcode in their client ID and client secret, 62 | // but the sample app allows the user to enter them in a text field, and 63 | // saves them in the preferences 64 | NSString *clientID = [defaults stringForKey:kGoogleClientIDKey]; 65 | NSString *clientSecret = [defaults stringForKey:kGoogleClientSecretKey]; 66 | 67 | GTMOAuth2Authentication *auth = nil; 68 | 69 | if (clientID && clientSecret) { 70 | auth = [GTMOAuth2WindowController authForGoogleFromKeychainForName:kKeychainItemName 71 | clientID:clientID 72 | clientSecret:clientSecret]; 73 | } 74 | 75 | if (auth.canAuthorize) { 76 | // Select the Google radio button 77 | [mRadioButtons selectCellWithTag:0]; 78 | } else { 79 | // There is no saved Google authentication 80 | // 81 | // Perhaps we have a saved authorization for DailyMotion instead; try getting 82 | // that from the keychain 83 | 84 | clientID = [defaults stringForKey:kDailyMotionClientIDKey]; 85 | clientSecret = [defaults stringForKey:kDailyMotionClientSecretKey]; 86 | 87 | if (clientID && clientSecret) { 88 | auth = [self authForDailyMotion]; 89 | if (auth) { 90 | auth.clientID = clientID; 91 | auth.clientSecret = clientSecret; 92 | 93 | BOOL didAuth = [GTMOAuth2WindowController authorizeFromKeychainForName:kDailyMotionKeychainItemName 94 | authentication:auth]; 95 | if (didAuth) { 96 | // select the DailyMotion radio button 97 | [mRadioButtons selectCellWithTag:1]; 98 | } 99 | } 100 | } 101 | } 102 | 103 | // Save the authentication object, which holds the auth tokens and 104 | // the scope string used to obtain the token. For Google services, 105 | // the auth object also holds the user's email address. 106 | [self setAuthentication:auth]; 107 | 108 | // Update the client ID value text fields to match the radio button selection 109 | [self loadClientIDValues]; 110 | 111 | // This is optional: 112 | // 113 | // We'll watch for the "hidden" fetches that occur to obtain tokens 114 | // during authentication, and start and stop our indeterminate progress 115 | // indicator during the fetches 116 | // 117 | // usually, these fetches are very brief 118 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 119 | [nc addObserver:self 120 | selector:@selector(signInFetchStateChanged:) 121 | name:kGTMOAuth2FetchStarted 122 | object:nil]; 123 | [nc addObserver:self 124 | selector:@selector(signInFetchStateChanged:) 125 | name:kGTMOAuth2FetchStopped 126 | object:nil]; 127 | [nc addObserver:self 128 | selector:@selector(signInNetworkLost:) 129 | name:kGTMOAuth2NetworkLost 130 | object:nil]; 131 | 132 | [self updateUI]; 133 | } 134 | 135 | - (void)dealloc { 136 | [mAuth release]; 137 | [super dealloc]; 138 | } 139 | 140 | - (BOOL)isGoogleButtonSelected { 141 | int tag = [[mRadioButtons selectedCell] tag]; 142 | return (tag == 0); 143 | } 144 | 145 | #pragma mark - 146 | 147 | - (IBAction)APIConsoleClicked:(id)sender { 148 | NSURL *url = [NSURL URLWithString:@"https://code.google.com/apis/console"]; 149 | [[NSWorkspace sharedWorkspace] openURL:url]; 150 | } 151 | 152 | - (IBAction)doAnAuthenticatedAPIFetchClicked:(id)sender { 153 | [self doAnAuthenticatedAPIFetch]; 154 | } 155 | 156 | - (IBAction)expireNowClicked:(id)sender { 157 | NSDate *date = mAuth.expirationDate; 158 | if (date == nil) { 159 | NSBeep(); 160 | } else { 161 | mAuth.expirationDate = [NSDate dateWithTimeIntervalSince1970:0]; 162 | [self updateUI]; 163 | } 164 | } 165 | 166 | - (IBAction)radioButtonClicked:(id)sender { 167 | [self loadClientIDValues]; 168 | } 169 | 170 | - (BOOL)isSignedIn { 171 | BOOL isSignedIn = mAuth.canAuthorize; 172 | return isSignedIn; 173 | } 174 | 175 | - (IBAction)signInOutClicked:(id)sender { 176 | if (![self isSignedIn]) { 177 | // Sign in 178 | [mAPIResultTextView setString:@"Authenticating..."]; 179 | 180 | if ([self isGoogleButtonSelected]) { 181 | [self signInToGoogle]; 182 | } else { 183 | [self signInToDailyMotion]; 184 | } 185 | } else { 186 | // Sign out 187 | [self signOut]; 188 | 189 | [mAPIResultTextView setString:@""]; 190 | } 191 | [self updateUI]; 192 | } 193 | 194 | - (void)signOut { 195 | if ([mAuth.serviceProvider isEqual:kGTMOAuth2ServiceProviderGoogle]) { 196 | // Remove the token from Google's servers 197 | [GTMOAuth2WindowController revokeTokenForGoogleAuthentication:mAuth]; 198 | } 199 | 200 | // Remove the stored Google authentication from the keychain, if any 201 | [GTMOAuth2WindowController removeAuthFromKeychainForName:kKeychainItemName]; 202 | 203 | // Remove the stored DailyMotion authentication from the keychain, if any 204 | [GTMOAuth2WindowController removeAuthFromKeychainForName:kDailyMotionKeychainItemName]; 205 | 206 | // Discard our retained authentication object 207 | [self setAuthentication:nil]; 208 | 209 | [self updateUI]; 210 | } 211 | 212 | - (void)signInToGoogle { 213 | [self signOut]; 214 | 215 | // For Google APIs, the scope strings are available 216 | // in the service constant header files. 217 | NSString *scope = @"https://www.googleapis.com/auth/plus.me"; 218 | 219 | // Typically, applications will hardcode the client ID and client secret 220 | // strings into the source code; they should not be user-editable or visible. 221 | NSString *clientID = [mClientIDField stringValue]; 222 | NSString *clientSecret = [mClientSecretField stringValue]; 223 | 224 | if ([clientID length] == 0 || [clientSecret length] == 0) { 225 | [self alertClientIDNeeded]; 226 | return; 227 | } 228 | 229 | // Display the autentication sheet 230 | GTMOAuth2WindowController *windowController; 231 | windowController = [GTMOAuth2WindowController controllerWithScope:scope 232 | clientID:clientID 233 | clientSecret:clientSecret 234 | keychainItemName:kKeychainItemName 235 | resourceBundle:nil]; 236 | 237 | // During display of the sign-in window, loss and regain of network 238 | // connectivity will be reported with the notifications 239 | // kGTMOAuth2NetworkLost/kGTMOAuth2NetworkFound 240 | // 241 | // See the method signInNetworkLost: for an example of handling 242 | // the notification. 243 | 244 | // Optional: Google servers allow specification of the sign-in display 245 | // language as an additional "hl" parameter to the authorization URL, 246 | // using BCP 47 language codes. 247 | // 248 | // For this sample, we'll force English as the display language. 249 | NSDictionary *params = [NSDictionary dictionaryWithObject:@"en" 250 | forKey:@"hl"]; 251 | windowController.signIn.additionalAuthorizationParameters = params; 252 | 253 | // Optional: display some html briefly before the sign-in page loads 254 | NSString *html = @"
Loading sign-in page...
"; 255 | windowController.initialHTMLString = html; 256 | 257 | // Most applications will not want the dialog to remember the signed-in user 258 | // across multiple sign-ins, but the sample app allows it. 259 | windowController.shouldPersistUser = [mPersistUserCheckbox state]; 260 | 261 | // By default, the controller will fetch the user's email, but not the rest of 262 | // the user's profile. The full profile can be requested from Google's server 263 | // by setting this property before sign-in: 264 | // 265 | // windowController.signIn.shouldFetchGoogleUserProfile = YES; 266 | // 267 | // The profile will be available after sign-in as 268 | // 269 | // NSDictionary *profile = windowController.signIn.userProfile; 270 | 271 | [windowController signInSheetModalForWindow:mMainWindow 272 | delegate:self 273 | finishedSelector:@selector(windowController:finishedWithAuth:error:)]; 274 | } 275 | 276 | - (GTMOAuth2Authentication *)authForDailyMotion { 277 | // http://www.dailymotion.com/doc/api/authentication.html 278 | NSURL *tokenURL = [NSURL URLWithString:@"https://api.dailymotion.com/oauth/token"]; 279 | 280 | // We'll make up an arbitrary redirectURI. The controller will watch for 281 | // the server to redirect the web view to this URI, but this URI will not be 282 | // loaded, so it need not be for any actual web page. 283 | NSString *redirectURI = @"http://www.google.com/OAuthCallback"; 284 | 285 | NSString *clientID = [mClientIDField stringValue]; 286 | NSString *clientSecret = [mClientSecretField stringValue]; 287 | 288 | GTMOAuth2Authentication *auth; 289 | auth = [GTMOAuth2Authentication authenticationWithServiceProvider:kDailyMotionServiceName 290 | tokenURL:tokenURL 291 | redirectURI:redirectURI 292 | clientID:clientID 293 | clientSecret:clientSecret]; 294 | return auth; 295 | } 296 | 297 | - (void)signInToDailyMotion { 298 | [self signOut]; 299 | 300 | GTMOAuth2Authentication *auth = [self authForDailyMotion]; 301 | auth.scope = @"read"; 302 | 303 | if ([auth.clientID length] == 0 || [auth.clientSecret length] == 0) { 304 | [self alertClientIDNeeded]; 305 | return; 306 | } 307 | 308 | // display the autentication sheet 309 | NSURL *authURL = [NSURL URLWithString:@"https://api.dailymotion.com/oauth/authorize?display=popup"]; 310 | 311 | GTMOAuth2WindowController *windowController; 312 | windowController = [GTMOAuth2WindowController controllerWithAuthentication:auth 313 | authorizationURL:authURL 314 | keychainItemName:kDailyMotionKeychainItemName 315 | resourceBundle:nil]; 316 | 317 | // optional: display some html briefly before the sign-in page loads 318 | NSString *html = @"
Loading sign-in page...
"; 319 | [windowController setInitialHTMLString:html]; 320 | 321 | windowController.shouldPersistUser = [mPersistUserCheckbox state]; 322 | 323 | [windowController signInSheetModalForWindow:mMainWindow 324 | delegate:self 325 | finishedSelector:@selector(windowController:finishedWithAuth:error:)]; 326 | } 327 | 328 | - (void)windowController:(GTMOAuth2WindowController *)windowController 329 | finishedWithAuth:(GTMOAuth2Authentication *)auth 330 | error:(NSError *)error { 331 | [mAPIResultTextView setString:@""]; 332 | 333 | if (error != nil) { 334 | // Authentication failed (perhaps the user denied access, or closed the 335 | // window before granting access) 336 | NSString *errorStr = [error localizedDescription]; 337 | 338 | NSData *responseData = [[error userInfo] objectForKey:@"data"]; // kGTMHTTPFetcherStatusDataKey 339 | if ([responseData length] > 0) { 340 | // Show the body of the server's authentication failure response 341 | errorStr = [[[NSString alloc] initWithData:responseData 342 | encoding:NSUTF8StringEncoding] autorelease]; 343 | } else { 344 | NSString *str = [[error userInfo] objectForKey:kGTMOAuth2ErrorMessageKey]; 345 | if ([str length] > 0) { 346 | errorStr = str; 347 | } 348 | } 349 | [mAPIResultTextView setString:errorStr]; 350 | 351 | [self setAuthentication:nil]; 352 | } else { 353 | // Authentication succeeded 354 | // 355 | // At this point, we either use the authentication object to explicitly 356 | // authorize requests, like 357 | // 358 | // [auth authorizeRequest:myNSURLMutableRequest 359 | // completionHandler:^(NSError *error) { 360 | // if (error == nil) { 361 | // // request here has been authorized 362 | // } 363 | // }]; 364 | // 365 | // or store the authentication object into a fetcher or a Google API service 366 | // object like 367 | // 368 | // [fetcher setAuthorizer:auth]; 369 | 370 | // save the authentication object 371 | [self setAuthentication:auth]; 372 | 373 | [mAPIResultTextView setString:@"Authentication succeeded"]; 374 | 375 | // We can also access custom server response parameters here. 376 | // 377 | // For example, DailyMotion's token endpoint returns a uid value: 378 | // 379 | // NSString *uid = [auth.parameters valueForKey:@"uid"]; 380 | } 381 | 382 | [self updateUI]; 383 | } 384 | 385 | - (void)alertClientIDNeeded { 386 | NSAlert *alert = [[[NSAlert alloc] init] autorelease]; 387 | alert.messageText = @"Error"; 388 | alert.informativeText = 389 | @"The sample code requires a valid client ID and client secret to sign in."; 390 | [alert beginSheetModalForWindow:mMainWindow 391 | completionHandler:nil]; 392 | } 393 | 394 | #pragma mark - 395 | 396 | - (void)doAnAuthenticatedAPIFetch { 397 | NSString *urlStr; 398 | if ([self isGoogleButtonSelected]) { 399 | // Google Plus feed 400 | urlStr = @"https://www.googleapis.com/plus/v1/people/me/activities/public"; 401 | } else { 402 | // DailyMotion user favorites feed 403 | urlStr = @"https://api.dailymotion.com/videos/favorites"; 404 | } 405 | 406 | [mAPIResultTextView setString:@"Doing an authenticated API fetch..."]; 407 | [mAPIResultTextView display]; 408 | 409 | NSURL *url = [NSURL URLWithString:urlStr]; 410 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 411 | [mAuth authorizeRequest:request 412 | completionHandler:^(NSError *error) { 413 | if (error) { 414 | [mAPIResultTextView setString:[error description]]; 415 | } else { 416 | // Synchronous fetches like this are a really bad idea in Cocoa applications 417 | // 418 | // For a very easy async alternative, we could use GTMHTTPFetcher 419 | NSError *error = nil; 420 | NSURLResponse *response = nil; 421 | NSData *data = [NSURLConnection sendSynchronousRequest:request 422 | returningResponse:&response 423 | error:&error]; 424 | if (data) { 425 | // API fetch succeeded 426 | NSString *str = [[[NSString alloc] initWithData:data 427 | encoding:NSUTF8StringEncoding] autorelease]; 428 | [mAPIResultTextView setString:str]; 429 | } else { 430 | // Fetch failed 431 | [mAPIResultTextView setString:[error description]]; 432 | } 433 | } 434 | 435 | // The access token may have changed 436 | [self updateUI]; 437 | }]; 438 | } 439 | 440 | #pragma mark - 441 | 442 | - (void)signInFetchStateChanged:(NSNotification *)note { 443 | // This just lets the user know something is happening during the 444 | // sign-in sequence's "invisible" fetches to obtain tokens 445 | // 446 | // The fetcher is available as 447 | // [[note userInfo] objectForKey:kGTMOAuth2FetcherKey] 448 | // 449 | // The type of token obtained is available on the start notification as 450 | // [[note userInfo] objectForKey:kGTMOAuth2FetchTypeKey] 451 | // 452 | if ([[note name] isEqual:kGTMOAuth2FetchStarted]) { 453 | mAuthFetchersRunningCount++; 454 | } else if (mAuthFetchersRunningCount > 0) { 455 | mAuthFetchersRunningCount--; 456 | } 457 | 458 | if (mAuthFetchersRunningCount > 0) { 459 | [mSpinner startAnimation:self]; 460 | } else { 461 | [mSpinner stopAnimation:self]; 462 | } 463 | } 464 | 465 | - (void)signInNetworkLost:(NSNotification *)note { 466 | // The network dropped for 30 seconds 467 | // 468 | // We could alert the user and wait for notification that the network has 469 | // has returned, or just cancel the sign-in sheet, as shown here 470 | GTMOAuth2SignIn *signIn = [note object]; 471 | GTMOAuth2WindowController *controller = [signIn delegate]; 472 | [controller cancelSigningIn]; 473 | } 474 | 475 | - (void)updateUI { 476 | // Update the text showing the signed-in state and the button title 477 | if ([self isSignedIn]) { 478 | // Signed in 479 | NSString *accessToken = mAuth.accessToken; 480 | NSString *refreshToken = mAuth.refreshToken; 481 | NSString *expiration = [mAuth.expirationDate description]; 482 | NSString *email = mAuth.userEmail; 483 | NSString *serviceName = mAuth.serviceProvider; 484 | 485 | BOOL isVerified = [mAuth.userEmailIsVerified boolValue]; 486 | if (!isVerified) { 487 | // Email address is not verified 488 | // 489 | // The email address is listed with the account info on the server, but 490 | // has not been confirmed as belonging to the owner of this account. 491 | email = [email stringByAppendingString:@" (unverified)"]; 492 | } 493 | 494 | [mAccessTokenField setStringValue:(accessToken != nil ? accessToken : @"")]; 495 | [mExpirationField setStringValue:(expiration != nil ? expiration : @"")]; 496 | [mRefreshTokenField setStringValue:(refreshToken != nil ? refreshToken : @"")]; 497 | [mUsernameField setStringValue:(email != nil ? email : @"")]; 498 | [mServiceNameField setStringValue:(serviceName != nil ? serviceName : @"")]; 499 | [mSignInOutButton setTitle:@"Sign Out"]; 500 | [mDoAnAuthenticatedFetchButton setEnabled:YES]; 501 | [mExpireNowButton setEnabled:YES]; 502 | } else { 503 | // Signed out 504 | [mUsernameField setStringValue:@"-Not signed in-"]; 505 | [mServiceNameField setStringValue:@""]; 506 | [mAccessTokenField setStringValue:@"-No token-"]; 507 | [mExpirationField setStringValue:@""]; 508 | [mRefreshTokenField setStringValue:@""]; 509 | [mSignInOutButton setTitle:@"Sign In..."]; 510 | [mDoAnAuthenticatedFetchButton setEnabled:NO]; 511 | [mExpireNowButton setEnabled:NO]; 512 | } 513 | } 514 | 515 | - (void)setAuthentication:(GTMOAuth2Authentication *)auth { 516 | [mAuth autorelease]; 517 | mAuth = [auth retain]; 518 | } 519 | 520 | #pragma mark Client ID and Secret 521 | 522 | // 523 | // Normally an application will hardwire the client ID and client secret 524 | // strings in the source code. This sample app has to allow them to be 525 | // entered by the developer, so we'll save them across runs into preferences. 526 | // 527 | 528 | - (void)saveClientIDValues { 529 | // Save the client ID and secret from the text fields into the prefs 530 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 531 | NSString *clientID = [mClientIDField stringValue]; 532 | NSString *clientSecret = [mClientSecretField stringValue]; 533 | 534 | if ([self isGoogleButtonSelected]) { 535 | [defaults setObject:clientID forKey:kGoogleClientIDKey]; 536 | [defaults setObject:clientSecret forKey:kGoogleClientSecretKey]; 537 | } else { 538 | [defaults setObject:clientID forKey:kDailyMotionClientIDKey]; 539 | [defaults setObject:clientSecret forKey:kDailyMotionClientSecretKey]; 540 | } 541 | } 542 | 543 | - (void)loadClientIDValues { 544 | // Load the client ID and secret from the prefs into the text fields 545 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 546 | NSString *clientID, *clientSecret; 547 | 548 | if ([self isGoogleButtonSelected]) { 549 | clientID = [defaults stringForKey:kGoogleClientIDKey]; 550 | clientSecret = [defaults stringForKey:kGoogleClientSecretKey]; 551 | } else { 552 | clientID = [defaults stringForKey:kDailyMotionClientIDKey]; 553 | clientSecret = [defaults stringForKey:kDailyMotionClientSecretKey]; 554 | } 555 | 556 | [mClientIDField setStringValue:(clientID ? clientID : @"")]; 557 | [mClientSecretField setStringValue:(clientSecret ? clientSecret : @"")]; 558 | } 559 | 560 | - (void)controlTextDidChange:(NSNotification *)note { 561 | [self saveClientIDValues]; 562 | } 563 | 564 | @end 565 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/OAuth2Sample_Prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | -------------------------------------------------------------------------------- /Examples/OAuth2Sample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | return NSApplicationMain(argc, (const char **) argv); 6 | } 7 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.example.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSMainNibFile 28 | OAuth2SampleMainTouch 29 | NSMainNibFile~ipad 30 | OAuth2SampleMainiPad 31 | 32 | 33 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleAppDelegateTouch.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // OAuth2SampleAppDelegateTouch.h - OAuth example for Cocoa Touch. 17 | 18 | @interface OAuth2SampleAppDelegateTouch : NSObject { 19 | @private 20 | UIWindow *mWindow; 21 | UINavigationController *mNavigationController; 22 | } 23 | 24 | @property (nonatomic, retain) IBOutlet UIWindow *window; 25 | @property (nonatomic, retain) IBOutlet UINavigationController *navigationController; 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleAppDelegateTouch.m: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // OAuth2SampleAppDelegateTouch.m 17 | 18 | #import "OAuth2SampleAppDelegateTouch.h" 19 | #import "OAuth2SampleRootViewControllerTouch.h" 20 | 21 | @implementation OAuth2SampleAppDelegateTouch 22 | 23 | @synthesize window = mWindow; 24 | @synthesize navigationController = mNavigationController; 25 | 26 | - (void)dealloc { 27 | [mNavigationController release]; 28 | [mWindow release]; 29 | [super dealloc]; 30 | } 31 | 32 | - (void)applicationDidFinishLaunching:(UIApplication *)application { 33 | [mWindow addSubview:[mNavigationController view]]; 34 | [mWindow makeKeyAndVisible]; 35 | } 36 | 37 | - (void)applicationWillTerminate:(UIApplication *)application { 38 | [[NSUserDefaults standardUserDefaults] synchronize]; 39 | } 40 | 41 | @end 42 | 43 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleRootViewControllerTouch.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // OAuth2SampleRootViewControllerTouch.h 17 | 18 | @class GTMOAuth2Authentication; 19 | 20 | @interface OAuth2SampleRootViewControllerTouch : UIViewController { 21 | UISegmentedControl *mServiceSegments; 22 | UITextField *mClientIDField; 23 | UITextField *mClientSecretField; 24 | 25 | UILabel *mServiceNameField; 26 | UILabel *mEmailField; 27 | UILabel *mAccessTokenField; 28 | UILabel *mExpirationField; 29 | UILabel *mRefreshTokenField; 30 | 31 | UIButton *mFetchButton; 32 | UIButton *mExpireNowButton; 33 | 34 | UISwitch *mShouldSaveInKeychainSwitch; 35 | 36 | UIBarButtonItem *mSignInOutButton; 37 | 38 | int mNetworkActivityCounter; 39 | GTMOAuth2Authentication *mAuth; 40 | } 41 | 42 | @property (nonatomic, retain) IBOutlet UITextField *clientIDField; 43 | @property (nonatomic, retain) IBOutlet UITextField *clientSecretField; 44 | @property (nonatomic, retain) IBOutlet UILabel *serviceNameField; 45 | @property (nonatomic, retain) IBOutlet UILabel *emailField; 46 | @property (nonatomic, retain) IBOutlet UILabel *accessTokenField; 47 | @property (nonatomic, retain) IBOutlet UILabel *expirationField; 48 | @property (nonatomic, retain) IBOutlet UILabel *refreshTokenField; 49 | @property (nonatomic, retain) IBOutlet UIButton *fetchButton; 50 | @property (nonatomic, retain) IBOutlet UIButton *expireNowButton; 51 | @property (nonatomic, retain) IBOutlet UISegmentedControl *serviceSegments; 52 | @property (nonatomic, retain) IBOutlet UISwitch *shouldSaveInKeychainSwitch; 53 | @property (nonatomic, retain) IBOutlet UIBarButtonItem *signInOutButton; 54 | 55 | @property (nonatomic, retain) GTMOAuth2Authentication *auth; 56 | 57 | - (IBAction)serviceSegmentClicked:(id)sender; 58 | - (IBAction)signInOutClicked:(id)sender; 59 | - (IBAction)fetchClicked:(id)sender; 60 | - (IBAction)expireNowClicked:(id)sender; 61 | - (IBAction)toggleShouldSaveInKeychain:(id)sender; 62 | 63 | - (void)signInToGoogle; 64 | - (void)signInToDailyMotion; 65 | - (void)signOut; 66 | - (BOOL)isSignedIn; 67 | 68 | - (void)updateUI; 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleRootViewControllerTouch.m: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // OAuth2SampleRootViewControllerTouch.m 17 | 18 | #import "OAuth2SampleRootViewControllerTouch.h" 19 | #import "GTMOAuth2ViewControllerTouch.h" 20 | #import "GTMOAuth2SignIn.h" 21 | 22 | static NSString *const kKeychainItemName = @"OAuth Sample: Google Contacts"; 23 | static NSString *const kShouldSaveInKeychainKey = @"shouldSaveInKeychain"; 24 | 25 | static NSString *const kDailyMotionAppServiceName = @"OAuth Sample: DailyMotion"; 26 | static NSString *const kDailyMotionServiceName = @"DailyMotion"; 27 | 28 | @implementation OAuth2SampleRootViewControllerTouch 29 | 30 | @synthesize clientIDField = mClientIDField, 31 | clientSecretField = mClientSecretField, 32 | serviceNameField = mServiceNameField, 33 | emailField = mEmailField, 34 | expirationField = mExpirationField, 35 | accessTokenField = mAccessTokenField, 36 | refreshTokenField = mRefreshTokenField, 37 | fetchButton = mFetchButton, 38 | expireNowButton = mExpireNowButton, 39 | serviceSegments = mServiceSegments, 40 | shouldSaveInKeychainSwitch = mShouldSaveInKeychainSwitch, 41 | signInOutButton = mSignInOutButton; 42 | 43 | @synthesize auth = mAuth; 44 | 45 | // NSUserDefaults keys 46 | static NSString *const kGoogleClientIDKey = @"GoogleClientID"; 47 | static NSString *const kGoogleClientSecretKey = @"GoogleClientSecret"; 48 | static NSString *const kDailyMotionClientIDKey = @"DailyMotionClientID"; 49 | static NSString *const kDailyMotionClientSecretKey = @"DailyMotionClientSecret"; 50 | 51 | - (void)awakeFromNib { 52 | [super awakeFromNib]; 53 | 54 | // Listen for network change notifications 55 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 56 | [nc addObserver:self selector:@selector(incrementNetworkActivity:) name:kGTMOAuth2WebViewStartedLoading object:nil]; 57 | [nc addObserver:self selector:@selector(decrementNetworkActivity:) name:kGTMOAuth2WebViewStoppedLoading object:nil]; 58 | [nc addObserver:self selector:@selector(incrementNetworkActivity:) name:kGTMOAuth2FetchStarted object:nil]; 59 | [nc addObserver:self selector:@selector(decrementNetworkActivity:) name:kGTMOAuth2FetchStopped object:nil]; 60 | [nc addObserver:self selector:@selector(signInNetworkLostOrFound:) name:kGTMOAuth2NetworkLost object:nil]; 61 | [nc addObserver:self selector:@selector(signInNetworkLostOrFound:) name:kGTMOAuth2NetworkFound object:nil]; 62 | 63 | // Fill in the Client ID and Client Secret text fields 64 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 65 | 66 | // First, we'll try to get the saved Google authentication, if any, from 67 | // the keychain 68 | 69 | // Normal applications will hardcode in their client ID and client secret, 70 | // but the sample app allows the user to enter them in a text field, and 71 | // saves them in the preferences 72 | NSString *clientID = [defaults stringForKey:kGoogleClientIDKey]; 73 | NSString *clientSecret = [defaults stringForKey:kGoogleClientSecretKey]; 74 | 75 | GTMOAuth2Authentication *auth = nil; 76 | 77 | if (clientID && clientSecret) { 78 | auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName 79 | clientID:clientID 80 | clientSecret:clientSecret]; 81 | } 82 | 83 | if (auth.canAuthorize) { 84 | // Select the Google service segment 85 | self.serviceSegments.selectedSegmentIndex = 0; 86 | } else { 87 | // There is no saved Google authentication 88 | // 89 | // Perhaps we have a saved authorization for DailyMotion instead; try getting 90 | // that from the keychain 91 | 92 | clientID = [defaults stringForKey:kDailyMotionClientIDKey]; 93 | clientSecret = [defaults stringForKey:kDailyMotionClientSecretKey]; 94 | 95 | if (clientID && clientSecret) { 96 | auth = [self authForDailyMotion]; 97 | if (auth) { 98 | auth.clientID = clientID; 99 | auth.clientSecret = clientSecret; 100 | 101 | BOOL didAuth = [GTMOAuth2ViewControllerTouch authorizeFromKeychainForName:kDailyMotionAppServiceName 102 | authentication:auth 103 | error:NULL]; 104 | if (didAuth) { 105 | // select the DailyMotion radio button 106 | self.serviceSegments.selectedSegmentIndex = 1; 107 | } 108 | } 109 | } 110 | } 111 | 112 | // Save the authentication object, which holds the auth tokens and 113 | // the scope string used to obtain the token. For Google services, 114 | // the auth object also holds the user's email address. 115 | self.auth = auth; 116 | 117 | // Update the client ID value text fields to match the radio button selection 118 | [self loadClientIDValues]; 119 | 120 | BOOL isRemembering = [self shouldSaveInKeychain]; 121 | self.shouldSaveInKeychainSwitch.on = isRemembering; 122 | [self updateUI]; 123 | } 124 | 125 | - (void)dealloc { 126 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 127 | 128 | [mServiceSegments release]; 129 | [mClientIDField release]; 130 | [mClientSecretField release]; 131 | [mServiceNameField release]; 132 | [mEmailField release]; 133 | [mAccessTokenField release]; 134 | [mExpirationField release]; 135 | [mRefreshTokenField release]; 136 | [mFetchButton release]; 137 | [mExpireNowButton release]; 138 | [mShouldSaveInKeychainSwitch release]; 139 | [mSignInOutButton release]; 140 | [mAuth release]; 141 | 142 | [super dealloc]; 143 | } 144 | 145 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation { 146 | // Returns non-zero on iPad, but backward compatible to SDKs earlier than 3.2. 147 | if (UI_USER_INTERFACE_IDIOM()) { 148 | return YES; 149 | } 150 | return [super shouldAutorotateToInterfaceOrientation:orientation]; 151 | } 152 | 153 | - (BOOL)isSignedIn { 154 | BOOL isSignedIn = self.auth.canAuthorize; 155 | return isSignedIn; 156 | } 157 | 158 | - (BOOL)isGoogleSegmentSelected { 159 | NSInteger segmentIndex = self.serviceSegments.selectedSegmentIndex; 160 | return (segmentIndex == 0); 161 | } 162 | 163 | - (IBAction)serviceSegmentClicked:(id)sender { 164 | [self loadClientIDValues]; 165 | } 166 | 167 | - (IBAction)signInOutClicked:(id)sender { 168 | [self saveClientIDValues]; 169 | 170 | if (![self isSignedIn]) { 171 | // Sign in 172 | if ([self isGoogleSegmentSelected]) { 173 | [self signInToGoogle]; 174 | } else { 175 | [self signInToDailyMotion]; 176 | } 177 | } else { 178 | // Sign out 179 | [self signOut]; 180 | } 181 | [self updateUI]; 182 | } 183 | 184 | - (IBAction)fetchClicked:(id)sender { 185 | // Just to prove we're signed in, we'll attempt an authenticated fetch for the 186 | // signed-in user 187 | [self doAnAuthenticatedAPIFetch]; 188 | } 189 | 190 | - (IBAction)expireNowClicked:(id)sender { 191 | NSDate *date = self.auth.expirationDate; 192 | if (date) { 193 | self.auth.expirationDate = [NSDate dateWithTimeIntervalSince1970:0]; 194 | [self updateUI]; 195 | } 196 | } 197 | 198 | // UISwitch does the toggling for us. We just need to read the state. 199 | - (IBAction)toggleShouldSaveInKeychain:(UISwitch *)sender { 200 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 201 | [defaults setBool:sender.isOn forKey:kShouldSaveInKeychainKey]; 202 | } 203 | 204 | - (void)signOut { 205 | if ([self.auth.serviceProvider isEqual:kGTMOAuth2ServiceProviderGoogle]) { 206 | // remove the token from Google's servers 207 | [GTMOAuth2ViewControllerTouch revokeTokenForGoogleAuthentication:self.auth]; 208 | } 209 | 210 | // remove the stored Google authentication from the keychain, if any 211 | [GTMOAuth2ViewControllerTouch removeAuthFromKeychainForName:kKeychainItemName]; 212 | 213 | // remove the stored DailyMotion authentication from the keychain, if any 214 | [GTMOAuth2ViewControllerTouch removeAuthFromKeychainForName:kDailyMotionAppServiceName]; 215 | 216 | // Discard our retained authentication object. 217 | self.auth = nil; 218 | 219 | [self updateUI]; 220 | } 221 | 222 | - (void)signInToGoogle { 223 | [self signOut]; 224 | 225 | NSString *keychainItemName = nil; 226 | if ([self shouldSaveInKeychain]) { 227 | keychainItemName = kKeychainItemName; 228 | } 229 | 230 | // For Google APIs, the scope strings are available 231 | // in the service constant header files. 232 | NSString *scope = @"https://www.googleapis.com/auth/plus.me"; 233 | 234 | // Typically, applications will hardcode the client ID and client secret 235 | // strings into the source code; they should not be user-editable or visible. 236 | // 237 | // But for this sample code, they are editable. 238 | NSString *clientID = self.clientIDField.text; 239 | NSString *clientSecret = self.clientSecretField.text; 240 | 241 | if ([clientID length] == 0 || [clientSecret length] == 0) { 242 | NSString *msg = @"The sample code requires a valid client ID and client secret to sign in."; 243 | [self displayAlertWithMessage:msg]; 244 | return; 245 | } 246 | 247 | // Note: 248 | // GTMOAuth2ViewControllerTouch is not designed to be reused. Make a new 249 | // one each time you are going to show it. 250 | 251 | // Display the autentication view. 252 | SEL finishedSel = @selector(viewController:finishedWithAuth:error:); 253 | 254 | GTMOAuth2ViewControllerTouch *viewController; 255 | viewController = [GTMOAuth2ViewControllerTouch controllerWithScope:scope 256 | clientID:clientID 257 | clientSecret:clientSecret 258 | keychainItemName:keychainItemName 259 | delegate:self 260 | finishedSelector:finishedSel]; 261 | 262 | // You can set the title of the navigationItem of the controller here, if you 263 | // want. 264 | 265 | // If the keychainItemName is not nil, the user's authorization information 266 | // will be saved to the keychain. By default, it saves with accessibility 267 | // kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, but that may be 268 | // customized here. For example, 269 | // 270 | // viewController.keychainItemAccessibility = kSecAttrAccessibleAlways; 271 | 272 | // During display of the sign-in window, loss and regain of network 273 | // connectivity will be reported with the notifications 274 | // kGTMOAuth2NetworkLost/kGTMOAuth2NetworkFound 275 | // 276 | // See the method signInNetworkLostOrFound: for an example of handling 277 | // the notification. 278 | 279 | // Optional: Google servers allow specification of the sign-in display 280 | // language as an additional "hl" parameter to the authorization URL, 281 | // using BCP 47 language codes. 282 | // 283 | // For this sample, we'll force English as the display language. 284 | NSDictionary *params = [NSDictionary dictionaryWithObject:@"en" 285 | forKey:@"hl"]; 286 | viewController.signIn.additionalAuthorizationParameters = params; 287 | 288 | // By default, the controller will fetch the user's email, but not the rest of 289 | // the user's profile. The full profile can be requested from Google's server 290 | // by setting this property before sign-in: 291 | // 292 | // viewController.signIn.shouldFetchGoogleUserProfile = YES; 293 | // 294 | // The profile will be available after sign-in as 295 | // 296 | // NSDictionary *profile = viewController.signIn.userProfile; 297 | 298 | // Optional: display some html briefly before the sign-in page loads 299 | NSString *html = @"
Loading sign-in page...
"; 300 | viewController.initialHTMLString = html; 301 | 302 | [[self navigationController] pushViewController:viewController animated:YES]; 303 | 304 | // The view controller will be popped before signing in has completed, as 305 | // there are some additional fetches done by the sign-in controller. 306 | // The kGTMOAuth2UserSignedIn notification will be posted to indicate 307 | // that the view has been popped and those additional fetches have begun. 308 | // It may be useful to display a temporary UI when kGTMOAuth2UserSignedIn is 309 | // posted, just until the finished selector is invoked. 310 | } 311 | 312 | - (GTMOAuth2Authentication *)authForDailyMotion { 313 | // http://www.dailymotion.com/doc/api/authentication.html 314 | NSURL *tokenURL = [NSURL URLWithString:@"https://api.dailymotion.com/oauth/token"]; 315 | 316 | // We'll make up an arbitrary redirectURI. The controller will watch for 317 | // the server to redirect the web view to this URI, but this URI will not be 318 | // loaded, so it need not be for any actual web page. 319 | NSString *redirectURI = @"http://www.google.com/OAuthCallback"; 320 | 321 | NSString *clientID = self.clientIDField.text; 322 | NSString *clientSecret = self.clientSecretField.text; 323 | 324 | GTMOAuth2Authentication *auth; 325 | auth = [GTMOAuth2Authentication authenticationWithServiceProvider:kDailyMotionServiceName 326 | tokenURL:tokenURL 327 | redirectURI:redirectURI 328 | clientID:clientID 329 | clientSecret:clientSecret]; 330 | return auth; 331 | } 332 | 333 | - (void)signInToDailyMotion { 334 | [self signOut]; 335 | 336 | GTMOAuth2Authentication *auth = [self authForDailyMotion]; 337 | auth.scope = @"read"; 338 | 339 | if ([auth.clientID length] == 0 || [auth.clientSecret length] == 0) { 340 | NSString *msg = @"The sample code requires a valid client ID and client secret to sign in."; 341 | [self displayAlertWithMessage:msg]; 342 | return; 343 | } 344 | 345 | NSString *keychainItemName = nil; 346 | if ([self shouldSaveInKeychain]) { 347 | keychainItemName = kKeychainItemName; 348 | } 349 | 350 | NSURL *authURL = [NSURL URLWithString:@"https://api.dailymotion.com/oauth/authorize?display=mobile"]; 351 | 352 | // Display the authentication view 353 | SEL sel = @selector(viewController:finishedWithAuth:error:); 354 | 355 | GTMOAuth2ViewControllerTouch *viewController; 356 | viewController = [GTMOAuth2ViewControllerTouch controllerWithAuthentication:auth 357 | authorizationURL:authURL 358 | keychainItemName:keychainItemName 359 | delegate:self 360 | finishedSelector:sel]; 361 | 362 | // We can set a URL for deleting the cookies after sign-in so the next time 363 | // the user signs in, the browser does not assume the user is already signed 364 | // in 365 | viewController.browserCookiesURL = [NSURL URLWithString:@"http://api.dailymotion.com/"]; 366 | 367 | // You can set the title of the navigationItem of the controller here, if you want 368 | 369 | // Now push our sign-in view 370 | [[self navigationController] pushViewController:viewController animated:YES]; 371 | } 372 | 373 | - (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController 374 | finishedWithAuth:(GTMOAuth2Authentication *)auth 375 | error:(NSError *)error { 376 | if (error != nil) { 377 | // Authentication failed (perhaps the user denied access, or closed the 378 | // window before granting access) 379 | NSLog(@"Authentication error: %@", error); 380 | NSData *responseData = [[error userInfo] objectForKey:@"data"]; // kGTMHTTPFetcherStatusDataKey 381 | if ([responseData length] > 0) { 382 | // show the body of the server's authentication failure response 383 | NSString *str = [[[NSString alloc] initWithData:responseData 384 | encoding:NSUTF8StringEncoding] autorelease]; 385 | NSLog(@"%@", str); 386 | } 387 | 388 | self.auth = nil; 389 | } else { 390 | // Authentication succeeded 391 | // 392 | // At this point, we either use the authentication object to explicitly 393 | // authorize requests, like 394 | // 395 | // [auth authorizeRequest:myNSURLMutableRequest 396 | // completionHandler:^(NSError *error) { 397 | // if (error == nil) { 398 | // // request here has been authorized 399 | // } 400 | // }]; 401 | // 402 | // or store the authentication object into a fetcher or a Google API service 403 | // object like 404 | // 405 | // [fetcher setAuthorizer:auth]; 406 | 407 | // save the authentication object 408 | self.auth = auth; 409 | } 410 | 411 | [self updateUI]; 412 | } 413 | 414 | - (void)doAnAuthenticatedAPIFetch { 415 | NSString *urlStr; 416 | if ([self isGoogleSegmentSelected]) { 417 | // Google Plus feed 418 | urlStr = @"https://www.googleapis.com/plus/v1/people/me/activities/public"; 419 | } else { 420 | // DailyMotion status feed 421 | urlStr = @"https://api.dailymotion.com/videos/favorites"; 422 | } 423 | 424 | NSURL *url = [NSURL URLWithString:urlStr]; 425 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 426 | [self.auth authorizeRequest:request 427 | completionHandler:^(NSError *authError) { 428 | if (authError) { 429 | [self displayAlertWithMessage:[authError description]]; 430 | } else { 431 | NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration ephemeralSessionConfiguration]; 432 | NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg 433 | delegate:nil 434 | delegateQueue:[NSOperationQueue mainQueue]]; 435 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request 436 | completionHandler:^(NSData *data, 437 | NSURLResponse *response, 438 | NSError *taskError) { 439 | NSString *output = nil; 440 | 441 | if (!taskError) { 442 | // API fetch succeeded 443 | output = [[[NSString alloc] initWithData:data 444 | encoding:NSUTF8StringEncoding] autorelease]; 445 | } else { 446 | // fetch failed 447 | output = [taskError description]; 448 | } 449 | [self displayAlertWithMessage:output]; 450 | [self updateUI]; 451 | }]; 452 | [task resume]; 453 | } 454 | }]; 455 | } 456 | 457 | #pragma mark - 458 | 459 | - (void)incrementNetworkActivity:(NSNotification *)notify { 460 | ++mNetworkActivityCounter; 461 | if (mNetworkActivityCounter == 1) { 462 | UIApplication *app = [UIApplication sharedApplication]; 463 | [app setNetworkActivityIndicatorVisible:YES]; 464 | } 465 | } 466 | 467 | - (void)decrementNetworkActivity:(NSNotification *)notify { 468 | --mNetworkActivityCounter; 469 | if (mNetworkActivityCounter == 0) { 470 | UIApplication *app = [UIApplication sharedApplication]; 471 | [app setNetworkActivityIndicatorVisible:NO]; 472 | } 473 | } 474 | 475 | - (void)signInNetworkLostOrFound:(NSNotification *)notify { 476 | if ([[notify name] isEqual:kGTMOAuth2NetworkLost]) { 477 | // network connection was lost; alert the user, or dismiss 478 | // the sign-in view with 479 | // [[[notify object] delegate] cancelSigningIn]; 480 | } else { 481 | // network connection was found again 482 | } 483 | } 484 | 485 | #pragma mark - 486 | 487 | - (void)updateUI { 488 | // update the text showing the signed-in state and the button title 489 | // A real program would use NSLocalizedString() for strings shown to the user. 490 | if ([self isSignedIn]) { 491 | // signed in 492 | self.serviceNameField.text = self.auth.serviceProvider; 493 | self.emailField.text = self.auth.userEmail; 494 | self.accessTokenField.text = self.auth.accessToken; 495 | self.expirationField.text = [self.auth.expirationDate description]; 496 | self.refreshTokenField.text = self.auth.refreshToken; 497 | 498 | self.signInOutButton.title = @"Sign Out"; 499 | self.fetchButton.enabled = YES; 500 | self.expireNowButton.enabled = YES; 501 | } else { 502 | // signed out 503 | self.serviceNameField.text = @"-Not signed in-"; 504 | self.emailField.text = @""; 505 | self.accessTokenField.text = @"-No access token-"; 506 | self.expirationField.text = @""; 507 | self.refreshTokenField.text = @"-No refresh token-"; 508 | 509 | self.signInOutButton.title = @"Sign In"; 510 | self.fetchButton.enabled = NO; 511 | self.expireNowButton.enabled = NO; 512 | } 513 | 514 | BOOL isRemembering = [self shouldSaveInKeychain]; 515 | self.shouldSaveInKeychainSwitch.on = isRemembering; 516 | } 517 | 518 | - (void)displayAlertWithMessage:(NSString *)message { 519 | UIAlertController *alert = 520 | [UIAlertController alertControllerWithTitle:@"OAuth2Sample" 521 | message:message 522 | preferredStyle:UIAlertControllerStyleAlert]; 523 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" 524 | style:UIAlertActionStyleDefault 525 | handler:nil]; 526 | [alert addAction:action]; 527 | [self presentViewController:alert animated:YES completion:nil]; 528 | } 529 | 530 | - (BOOL)textFieldShouldReturn:(UITextField *)textField { 531 | [textField resignFirstResponder]; 532 | return YES; 533 | } 534 | 535 | - (void)textFieldDidEndEditing:(UITextField *)textField { 536 | [self saveClientIDValues]; 537 | } 538 | 539 | - (BOOL)shouldSaveInKeychain { 540 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 541 | BOOL flag = [defaults boolForKey:kShouldSaveInKeychainKey]; 542 | return flag; 543 | } 544 | 545 | #pragma mark Client ID and Secret 546 | 547 | // 548 | // Normally an application will hardwire the client ID and client secret 549 | // strings in the source code. This sample app has to allow them to be 550 | // entered by the developer, so we'll save them across runs into preferences. 551 | // 552 | 553 | - (void)saveClientIDValues { 554 | // Save the client ID and secret from the text fields into the prefs 555 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 556 | NSString *clientID = self.clientIDField.text; 557 | NSString *clientSecret = self.clientSecretField.text; 558 | 559 | if ([self isGoogleSegmentSelected]) { 560 | [defaults setObject:clientID forKey:kGoogleClientIDKey]; 561 | [defaults setObject:clientSecret forKey:kGoogleClientSecretKey]; 562 | } else { 563 | [defaults setObject:clientID forKey:kDailyMotionClientIDKey]; 564 | [defaults setObject:clientSecret forKey:kDailyMotionClientSecretKey]; 565 | } 566 | } 567 | 568 | - (void)loadClientIDValues { 569 | // Load the client ID and secret from the prefs into the text fields 570 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 571 | 572 | if ([self isGoogleSegmentSelected]) { 573 | self.clientIDField.text = [defaults stringForKey:kGoogleClientIDKey]; 574 | self.clientSecretField.text = [defaults stringForKey:kGoogleClientSecretKey]; 575 | } else { 576 | self.clientIDField.text = [defaults stringForKey:kDailyMotionClientIDKey]; 577 | self.clientSecretField.text = [defaults stringForKey:kDailyMotionClientSecretKey]; 578 | } 579 | } 580 | 581 | @end 582 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleTouch.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1301BB45119CB4B100A33D6C /* OAuth2SampleMainiPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1301BB44119CB4B100A33D6C /* OAuth2SampleMainiPad.xib */; }; 11 | 13E350A0119CA9AE00B2EB5A /* mainTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C82E9211373B3000ACAEDD /* mainTouch.m */; }; 12 | 13E350A1119CA9AE00B2EB5A /* OAuth2SampleAppDelegateTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C82E9411373B3000ACAEDD /* OAuth2SampleAppDelegateTouch.m */; }; 13 | 13E350A2119CA9AE00B2EB5A /* OAuth2SampleRootViewControllerTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C82E9611373B3000ACAEDD /* OAuth2SampleRootViewControllerTouch.m */; }; 14 | 13E350A8119CA9AE00B2EB5A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 15 | 13E350A9119CA9AE00B2EB5A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 16 | 13E350AA119CA9AE00B2EB5A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */; }; 17 | 13E350AB119CA9AE00B2EB5A /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1397AC501137075C00DE52FD /* Security.framework */; }; 18 | 13E350AC119CA9AE00B2EB5A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FF23DC011754C0300E96C5A /* SystemConfiguration.framework */; }; 19 | 4F2447F3121F227900FEE1DA /* GTMOAuth2ViewControllerTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F2447D6121F211300FEE1DA /* GTMOAuth2ViewControllerTouch.m */; }; 20 | 4F244813121F263E00FEE1DA /* OAuth2SampleMainTouch.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F244810121F253B00FEE1DA /* OAuth2SampleMainTouch.xib */; }; 21 | 4F64A4071305E3F90058B9BA /* GTMOAuth2Authentication.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F64A4041305E3F90058B9BA /* GTMOAuth2Authentication.m */; }; 22 | 4F64A4081305E3F90058B9BA /* GTMOAuth2SignIn.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F64A4061305E3F90058B9BA /* GTMOAuth2SignIn.m */; }; 23 | 4FB495671CAB2398000D1487 /* GTMSessionFetcherIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FB495661CAB2398000D1487 /* GTMSessionFetcherIOS.framework */; }; 24 | 4FEED6C512233486006FB4BC /* GTMOAuth2ViewTouch.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F2447D7121F211300FEE1DA /* GTMOAuth2ViewTouch.xib */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 1301BB44119CB4B100A33D6C /* OAuth2SampleMainiPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OAuth2SampleMainiPad.xib; sourceTree = ""; }; 29 | 1397AC501137075C00DE52FD /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 30 | 13C82E9211373B3000ACAEDD /* mainTouch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = mainTouch.m; sourceTree = ""; }; 31 | 13C82E9311373B3000ACAEDD /* OAuth2SampleAppDelegateTouch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAuth2SampleAppDelegateTouch.h; sourceTree = ""; }; 32 | 13C82E9411373B3000ACAEDD /* OAuth2SampleAppDelegateTouch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAuth2SampleAppDelegateTouch.m; sourceTree = ""; }; 33 | 13C82E9511373B3000ACAEDD /* OAuth2SampleRootViewControllerTouch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAuth2SampleRootViewControllerTouch.h; sourceTree = ""; }; 34 | 13C82E9611373B3000ACAEDD /* OAuth2SampleRootViewControllerTouch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAuth2SampleRootViewControllerTouch.m; sourceTree = ""; }; 35 | 13C82E9711373B3000ACAEDD /* prefixTouch.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = prefixTouch.pch; sourceTree = ""; }; 36 | 13E350B0119CA9AE00B2EB5A /* OAuthSampleTouch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OAuthSampleTouch.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 13FA93DF113867A700E1AAA4 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.txt; sourceTree = ""; }; 38 | 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 39 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 40 | 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 41 | 4F2447D5121F211300FEE1DA /* GTMOAuth2ViewControllerTouch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMOAuth2ViewControllerTouch.h; path = ../../Source/Touch/GTMOAuth2ViewControllerTouch.h; sourceTree = SOURCE_ROOT; }; 42 | 4F2447D6121F211300FEE1DA /* GTMOAuth2ViewControllerTouch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMOAuth2ViewControllerTouch.m; path = ../../Source/Touch/GTMOAuth2ViewControllerTouch.m; sourceTree = SOURCE_ROOT; }; 43 | 4F2447D7121F211300FEE1DA /* GTMOAuth2ViewTouch.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = GTMOAuth2ViewTouch.xib; path = ../../Source/Touch/GTMOAuth2ViewTouch.xib; sourceTree = SOURCE_ROOT; }; 44 | 4F244810121F253B00FEE1DA /* OAuth2SampleMainTouch.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OAuth2SampleMainTouch.xib; sourceTree = ""; }; 45 | 4F64A4031305E3F90058B9BA /* GTMOAuth2Authentication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMOAuth2Authentication.h; path = ../../Source/GTMOAuth2Authentication.h; sourceTree = SOURCE_ROOT; }; 46 | 4F64A4041305E3F90058B9BA /* GTMOAuth2Authentication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMOAuth2Authentication.m; path = ../../Source/GTMOAuth2Authentication.m; sourceTree = SOURCE_ROOT; }; 47 | 4F64A4051305E3F90058B9BA /* GTMOAuth2SignIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMOAuth2SignIn.h; path = ../../Source/GTMOAuth2SignIn.h; sourceTree = SOURCE_ROOT; }; 48 | 4F64A4061305E3F90058B9BA /* GTMOAuth2SignIn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMOAuth2SignIn.m; path = ../../Source/GTMOAuth2SignIn.m; sourceTree = SOURCE_ROOT; }; 49 | 4FB495661CAB2398000D1487 /* GTMSessionFetcherIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GTMSessionFetcherIOS.framework; path = "../../Deps/gtm-session-fetcher/Source/build/Debug-iphoneos/GTMSessionFetcherIOS.framework"; sourceTree = ""; }; 50 | 4FF23DC011754C0300E96C5A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 51 | F49B45D213315AA200EAA732 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 13E350A7119CA9AE00B2EB5A /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | 4FB495671CAB2398000D1487 /* GTMSessionFetcherIOS.framework in Frameworks */, 60 | 13E350A8119CA9AE00B2EB5A /* Foundation.framework in Frameworks */, 61 | 13E350A9119CA9AE00B2EB5A /* UIKit.framework in Frameworks */, 62 | 13E350AA119CA9AE00B2EB5A /* CoreGraphics.framework in Frameworks */, 63 | 13E350AB119CA9AE00B2EB5A /* Security.framework in Frameworks */, 64 | 13E350AC119CA9AE00B2EB5A /* SystemConfiguration.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 13C82E9111373B3000ACAEDD /* Sample App Classes */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 13C82E9311373B3000ACAEDD /* OAuth2SampleAppDelegateTouch.h */, 75 | 13C82E9411373B3000ACAEDD /* OAuth2SampleAppDelegateTouch.m */, 76 | 13C82E9511373B3000ACAEDD /* OAuth2SampleRootViewControllerTouch.h */, 77 | 13C82E9611373B3000ACAEDD /* OAuth2SampleRootViewControllerTouch.m */, 78 | 13C82E9B11373B6A00ACAEDD /* Sample App Resources */, 79 | ); 80 | name = "Sample App Classes"; 81 | sourceTree = ""; 82 | }; 83 | 13C82E9B11373B6A00ACAEDD /* Sample App Resources */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 4F244810121F253B00FEE1DA /* OAuth2SampleMainTouch.xib */, 87 | 1301BB44119CB4B100A33D6C /* OAuth2SampleMainiPad.xib */, 88 | F49B45D213315AA200EAA732 /* Info.plist */, 89 | ); 90 | name = "Sample App Resources"; 91 | sourceTree = ""; 92 | }; 93 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 13E350B0119CA9AE00B2EB5A /* OAuthSampleTouch.app */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 13FA93DF113867A700E1AAA4 /* README.txt */, 105 | 13C82E9111373B3000ACAEDD /* Sample App Classes */, 106 | 4F41065D1149BCAE009202D9 /* OAuth Classes and Resources */, 107 | 29B97315FDCFA39411CA2CEA /* Other Sample App Sources */, 108 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 109 | 19C28FACFE9D520D11CA2CBB /* Products */, 110 | ); 111 | name = CustomTemplate; 112 | sourceTree = ""; 113 | }; 114 | 29B97315FDCFA39411CA2CEA /* Other Sample App Sources */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 13C82E9211373B3000ACAEDD /* mainTouch.m */, 118 | 13C82E9711373B3000ACAEDD /* prefixTouch.pch */, 119 | ); 120 | name = "Other Sample App Sources"; 121 | sourceTree = ""; 122 | }; 123 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 4FB495661CAB2398000D1487 /* GTMSessionFetcherIOS.framework */, 127 | 4FF23DC011754C0300E96C5A /* SystemConfiguration.framework */, 128 | 1D30AB110D05D00D00671497 /* Foundation.framework */, 129 | 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */, 130 | 1397AC501137075C00DE52FD /* Security.framework */, 131 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 132 | ); 133 | name = Frameworks; 134 | sourceTree = ""; 135 | }; 136 | 4F41065D1149BCAE009202D9 /* OAuth Classes and Resources */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 4F2447D5121F211300FEE1DA /* GTMOAuth2ViewControllerTouch.h */, 140 | 4F2447D6121F211300FEE1DA /* GTMOAuth2ViewControllerTouch.m */, 141 | 4F64A4031305E3F90058B9BA /* GTMOAuth2Authentication.h */, 142 | 4F64A4041305E3F90058B9BA /* GTMOAuth2Authentication.m */, 143 | 4F64A4051305E3F90058B9BA /* GTMOAuth2SignIn.h */, 144 | 4F64A4061305E3F90058B9BA /* GTMOAuth2SignIn.m */, 145 | 4F2447D7121F211300FEE1DA /* GTMOAuth2ViewTouch.xib */, 146 | ); 147 | name = "OAuth Classes and Resources"; 148 | sourceTree = ""; 149 | }; 150 | /* End PBXGroup section */ 151 | 152 | /* Begin PBXNativeTarget section */ 153 | 13E3509B119CA9AE00B2EB5A /* OAuthSampleTouch */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 13E350AD119CA9AE00B2EB5A /* Build configuration list for PBXNativeTarget "OAuthSampleTouch" */; 156 | buildPhases = ( 157 | 13E3509C119CA9AE00B2EB5A /* Resources */, 158 | 13E3509F119CA9AE00B2EB5A /* Sources */, 159 | 13E350A7119CA9AE00B2EB5A /* Frameworks */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | ); 165 | name = OAuthSampleTouch; 166 | productName = OAuthTouchSample; 167 | productReference = 13E350B0119CA9AE00B2EB5A /* OAuthSampleTouch.app */; 168 | productType = "com.apple.product-type.application"; 169 | }; 170 | /* End PBXNativeTarget section */ 171 | 172 | /* Begin PBXProject section */ 173 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 174 | isa = PBXProject; 175 | attributes = { 176 | LastUpgradeCheck = 0430; 177 | }; 178 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "OAuth2SampleTouch" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = English; 181 | hasScannedForEncodings = 1; 182 | knownRegions = ( 183 | English, 184 | Japanese, 185 | French, 186 | German, 187 | en, 188 | ); 189 | mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 13E3509B119CA9AE00B2EB5A /* OAuthSampleTouch */, 194 | ); 195 | }; 196 | /* End PBXProject section */ 197 | 198 | /* Begin PBXResourcesBuildPhase section */ 199 | 13E3509C119CA9AE00B2EB5A /* Resources */ = { 200 | isa = PBXResourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | 4FEED6C512233486006FB4BC /* GTMOAuth2ViewTouch.xib in Resources */, 204 | 4F244813121F263E00FEE1DA /* OAuth2SampleMainTouch.xib in Resources */, 205 | 1301BB45119CB4B100A33D6C /* OAuth2SampleMainiPad.xib in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXSourcesBuildPhase section */ 212 | 13E3509F119CA9AE00B2EB5A /* Sources */ = { 213 | isa = PBXSourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | 13E350A0119CA9AE00B2EB5A /* mainTouch.m in Sources */, 217 | 13E350A1119CA9AE00B2EB5A /* OAuth2SampleAppDelegateTouch.m in Sources */, 218 | 13E350A2119CA9AE00B2EB5A /* OAuth2SampleRootViewControllerTouch.m in Sources */, 219 | 4F2447F3121F227900FEE1DA /* GTMOAuth2ViewControllerTouch.m in Sources */, 220 | 4F64A4071305E3F90058B9BA /* GTMOAuth2Authentication.m in Sources */, 221 | 4F64A4081305E3F90058B9BA /* GTMOAuth2SignIn.m in Sources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXSourcesBuildPhase section */ 226 | 227 | /* Begin XCBuildConfiguration section */ 228 | 13E350AE119CA9AE00B2EB5A /* Debug */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | ALWAYS_SEARCH_USER_PATHS = NO; 232 | COPY_PHASE_STRIP = NO; 233 | GCC_DYNAMIC_NO_PIC = NO; 234 | GCC_OPTIMIZATION_LEVEL = 0; 235 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 236 | GCC_PREFIX_HEADER = prefixTouch.pch; 237 | GCC_PREPROCESSOR_DEFINITIONS = ( 238 | "GTM_OAUTH2_USE_FRAMEWORK_IMPORTS=1", 239 | "GTM_OAUTH2_USE_PLATFORM_FRAMEWORK=1", 240 | ); 241 | INFOPLIST_FILE = Info.plist; 242 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 243 | PRODUCT_NAME = OAuthSampleTouch; 244 | TARGETED_DEVICE_FAMILY = "1,2"; 245 | }; 246 | name = Debug; 247 | }; 248 | 13E350AF119CA9AE00B2EB5A /* Release */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | COPY_PHASE_STRIP = YES; 253 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 254 | GCC_PREFIX_HEADER = prefixTouch.pch; 255 | GCC_PREPROCESSOR_DEFINITIONS = ( 256 | "GTM_OAUTH2_USE_FRAMEWORK_IMPORTS=1", 257 | "GTM_OAUTH2_USE_PLATFORM_FRAMEWORK=1", 258 | ); 259 | INFOPLIST_FILE = Info.plist; 260 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 261 | PRODUCT_NAME = OAuthSampleTouch; 262 | TARGETED_DEVICE_FAMILY = "1,2"; 263 | }; 264 | name = Release; 265 | }; 266 | C01FCF4F08A954540054247B /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 270 | GCC_C_LANGUAGE_STANDARD = c99; 271 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 272 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 274 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 275 | GCC_WARN_SHADOW = YES; 276 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 277 | GCC_WARN_UNDECLARED_SELECTOR = YES; 278 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 279 | GCC_WARN_UNUSED_FUNCTION = YES; 280 | GCC_WARN_UNUSED_LABEL = YES; 281 | GCC_WARN_UNUSED_VARIABLE = YES; 282 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 283 | ONLY_ACTIVE_ARCH = YES; 284 | OTHER_CFLAGS = "-DDEBUG=1"; 285 | OTHER_LDFLAGS = "-ObjC"; 286 | SDKROOT = iphoneos; 287 | "WARNING_CFLAGS[arch=*]" = ( 288 | "-Wall", 289 | "-Werror", 290 | ); 291 | }; 292 | name = Debug; 293 | }; 294 | C01FCF5008A954540054247B /* Release */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 298 | GCC_C_LANGUAGE_STANDARD = c99; 299 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 300 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 302 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 303 | GCC_WARN_SHADOW = YES; 304 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 305 | GCC_WARN_UNDECLARED_SELECTOR = YES; 306 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 307 | GCC_WARN_UNUSED_FUNCTION = YES; 308 | GCC_WARN_UNUSED_LABEL = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 311 | OTHER_LDFLAGS = "-ObjC"; 312 | SDKROOT = iphoneos; 313 | "WARNING_CFLAGS[arch=*]" = ( 314 | "-Wall", 315 | "-Werror", 316 | ); 317 | }; 318 | name = Release; 319 | }; 320 | /* End XCBuildConfiguration section */ 321 | 322 | /* Begin XCConfigurationList section */ 323 | 13E350AD119CA9AE00B2EB5A /* Build configuration list for PBXNativeTarget "OAuthSampleTouch" */ = { 324 | isa = XCConfigurationList; 325 | buildConfigurations = ( 326 | 13E350AE119CA9AE00B2EB5A /* Debug */, 327 | 13E350AF119CA9AE00B2EB5A /* Release */, 328 | ); 329 | defaultConfigurationIsVisible = 0; 330 | defaultConfigurationName = Release; 331 | }; 332 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "OAuth2SampleTouch" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | C01FCF4F08A954540054247B /* Debug */, 336 | C01FCF5008A954540054247B /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | /* End XCConfigurationList section */ 342 | }; 343 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 344 | } 345 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleTouch.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleTouch.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/OAuth2SampleTouch.xcworkspace/xcshareddata/xcschemes/OAuthSampleTouch.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/README.txt: -------------------------------------------------------------------------------- 1 | This sample demonstrates the OAuth 2 view controllers for iOS 3.2 and higher. 2 | 3 | This sample uses a UINavigationController in its main view. 4 | 5 | If your application's main view doesn't use UINavigationController, consider using your UIViewController's presentModalViewController: to push a new cover view that is a UINavigationController. 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/mainTouch.m: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | #import 16 | 17 | int main(int argc, char *argv[]) { 18 | 19 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 20 | int retVal = UIApplicationMain(argc, argv, nil, nil); 21 | [pool release]; 22 | return retVal; 23 | } 24 | -------------------------------------------------------------------------------- /Examples/OAuth2SampleTouch/prefixTouch.pch: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | #import 16 | 17 | 18 | #ifdef __OBJC__ 19 | #import 20 | #import 21 | #endif 22 | -------------------------------------------------------------------------------- /GTMOAuth2.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'GTMOAuth2' 3 | s.version = '1.1.6' 4 | s.author = 'Google Inc.' 5 | s.homepage = 'https://github.com/google/gtm-oauth2' 6 | s.license = { :type => 'Apache', :file => 'LICENSE' } 7 | s.source = { :git => 'https://github.com/google/gtm-oauth2.git', 8 | :tag => "v#{s.version}" } 9 | s.summary = 'Google Toolbox for Mac - OAuth 2 Controllers' 10 | s.description = <<-DESC 11 | The Google Toolbox for Mac OAuth 2 Controllers make it easy for Cocoa 12 | applications to sign in to services using OAuth 2 for authentication 13 | and authorization. 14 | 15 | This version can be used with iOS ≥ 7.0 or OS X ≥ 10.9. 16 | DESC 17 | 18 | s.ios.deployment_target = '7.0' 19 | s.osx.deployment_target = '10.9' 20 | s.requires_arc = false 21 | 22 | s.source_files = 'Source/*.{h,m}' 23 | s.ios.source_files = 'Source/Touch/*.{h,m}' 24 | s.ios.resources = 'Source/Touch/*.xib' 25 | s.osx.source_files = 'Source/Mac/*.{h,m}' 26 | s.osx.resources = 'Source/Mac/*.xib' 27 | 28 | s.user_target_xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GTM_OAUTH2_USE_FRAMEWORK_IMPORTS=1' } 29 | 30 | s.frameworks = 'Security', 'SystemConfiguration' 31 | s.dependency 'GTMSessionFetcher', '~> 1.1' 32 | end 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > ## :warning: Deprecation Notice 2 | > Google has [deprecated](https://goo.gl/BkYjsP) the use of embedded web-views 3 | > for OAuth, which this library relies on. When making OAuth requests to Google, 4 | > use the replacement [GTMAppAuth](https://github.com/google/GTMAppAuth) 5 | > library. 6 | 7 | # GTM OAuth 2: Google Toolbox for Mac - OAuth 2 Controllers # 8 | 9 | **Project site**
10 | **Discussion group** 11 | 12 | The Google Toolbox for Mac OAuth 2 Controllers make it easy for Cocoa 13 | applications to sign in to services using OAuth 2 for authentication and 14 | authorization. 15 | 16 | Features include: 17 | - Complete embedded user interface using WebKit 18 | - Works with Google APIs and with any standard OAuth 2 provider 19 | - Handles sign-in, keychain storage of authorization token, and authorizing of requests 20 | - Independent of other projects 21 | 22 | **To get started** with GTM OAuth 2, read the [wiki](https://github.com/google/gtm-oauth2/wiki). 23 | 24 | **If you have a problem** or want a new feature to be included in the library, 25 | please join the 26 | [GTM-OAuth 2 discussion group](http://groups.google.com/group/gtm-oauth2) 27 | or submit an [issue](https://github.com/google/gtm-oauth2/issues). 28 | 29 | The library incorporates the 30 | [GTM Session Fetcher project](https://github.com/google/gtm-session-fetcher/). 31 | 32 | There is a separate project for [OAuth 1 controllers](https://github.com/google/gtm-oauth). 33 | 34 | Other useful classes for Mac and iOS developers are available in the 35 | [Google Toolbox for Mac](https://github.com/google/google-toolbox-for-mac). 36 | -------------------------------------------------------------------------------- /ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # GTM OAuth 2: Google Toolbox for Mac - OAuth 2 Controllers # 2 | 3 | ## Release History ## 4 | 5 | 6 | ### 7-August-2012 ### 7 | 8 | Replaced initial html delay fix with a pending request mechanism. 9 | 10 | 11 | ### 26-July-2012 ### 12 | 13 | Added popViewBlock property to GTMOAuth2ViewControllerTouch to allow 14 | dismissing the view when it lacks a navigation controller. 15 | 16 | 17 | ### 13-April-2012 ### 18 | 19 | Support setting keychain accessibility for iOS. 20 | 21 | 22 | ### 4-April-2012 ### 23 | 24 | Add scope to persistenceResponseString so applications can inspect 25 | the prior scope after loading the token from the keychain. 26 | 27 | 28 | ### 3-April-2012 ### 29 | 30 | Now pulls version 2.3.2 of SBJSON sources from github. 31 | 32 | 33 | ### 14-March-2012 ### 34 | 35 | Added notification on access token refresh. 36 | 37 | 38 | ### 7-October-2011 ### 39 | 40 | Added notifications for starting and stopping loads of UIWebView. 41 | 42 | 43 | ### 30-September-2011 ### 44 | 45 | Add brief delay to view controller to allow initial html to load. 46 | 47 | 48 | ### 21-September-2011 ### 49 | 50 | Added additionalTokenRequestParameters property to the authorization object. 51 | 52 | Updated the domain for clearing cookies when signing into Google services. 53 | 54 | 55 | ### 7-September-2011 ### 56 | 57 | Add support for token servers providing url-encoded responses (thanks ivo) 58 | 59 | 60 | ### 25-August-2011 ### 61 | 62 | Added convenience methods for creating autoreleased controllers. 63 | 64 | 65 | ### 24-August-2011 ### 66 | 67 | Fixed sign-in when Mac window controller displays as a standalone 68 | modal window (thanks mirko) 69 | 70 | 71 | ### 2-August-2011 ### 72 | 73 | Projects may now define GTM_OAUTH2_SKIP_GOOGLE_SUPPORT to exclude 74 | Google-specific code. The GTMOAuth2 project file also now includes 75 | "non-Google" targets for building without Google-specific code. 76 | 77 | 78 | ### 18-July-2011 ### 79 | 80 | The authorization header now uses the "Bearer" prefix. 81 | 82 | 83 | ### 12-July-2011 ### 84 | 85 | Added an additionalAuthorizationParameters property to the SignIn class, used 86 | by the sample apps to specify a display language for the sign-in pages. 87 | 88 | 89 | ### 20-June-2011 ### 90 | 91 | To avoid accidental leakage of tokens, the authentication object now 92 | returns kGTMOAuth2ErrorUnauthorizableRequest when attempting to authorize 93 | requests with schemes other than https. The property 94 | shouldAuthorizeAllRequests can be set to override this and allow any 95 | request to be authorized. 96 | 97 | 98 | ### 1-June-2011 ### 99 | 100 | Added Mac window controller property shouldAllowApplicationTermination 101 | 102 | Added user properties to window & view controllers. 103 | 104 | Fetchers may now optionally be created by a GTMHTTPFetcherService instance. 105 | 106 | 107 | ### 24-May-2011 ### 108 | 109 | Mac window controller now opens pop-up window links in an external browser 110 | by default, and provides an externalRequestSelector property to let 111 | the client provide custom handling. 112 | 113 | 114 | ### 4-May-2011 ### 115 | 116 | Addded support for services which provide access tokens, but no 117 | refresh tokens. 118 | 119 | 120 | ### Release 1.0.0 - 6-April-2011 ### 121 | 122 | Initial public release. 123 | -------------------------------------------------------------------------------- /Source/GTMOAuth2Authentication.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 17 | 18 | // This class implements the OAuth 2 protocol for authorizing requests. 19 | // http://tools.ietf.org/html/draft-ietf-oauth-v2 20 | 21 | #import 22 | 23 | #if (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11) \ 24 | || (TARGET_OS_IPHONE && defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0) 25 | #ifndef GTM_USE_SESSION_FETCHER 26 | #define GTM_USE_SESSION_FETCHER 1 27 | #endif 28 | 29 | #define GTMOAUTH2AUTHENTICATION_DEPRECATE_OLD_ENUMS 1 30 | #endif 31 | 32 | #ifndef GTM_OAUTH2_USE_FRAMEWORK_IMPORTS 33 | #define GTM_OAUTH2_USE_FRAMEWORK_IMPORTS 0 34 | #endif 35 | 36 | #ifndef GTM_OAUTH2_USE_PLATFORM_FRAMEWORK 37 | #define GTM_OAUTH2_USE_PLATFORM_FRAMEWORK 0 38 | #endif 39 | 40 | #if GTM_USE_SESSION_FETCHER 41 | #if GTM_OAUTH2_USE_FRAMEWORK_IMPORTS 42 | #if GTM_OAUTH2_USE_PLATFORM_FRAMEWORK 43 | // App project file use. 44 | #if TARGET_OS_IPHONE 45 | #import 46 | #else 47 | #import 48 | #endif // TARGET_OS_IPHONE 49 | #else 50 | // Cocoapod use. 51 | #import 52 | #endif // GTM_OAUTH2_USE_PLATFORM_FRAMEWORK 53 | #else 54 | #import "GTMSessionFetcher.h" 55 | #endif // GTM_OAUTH2_USE_FRAMEWORK_IMPORTS 56 | #else 57 | #if GTM_OAUTH2_USE_FRAMEWORK_IMPORTS 58 | #error GTMHTTPFetcher lacks a framework build 59 | #else 60 | #import "GTMHTTPFetcher.h" 61 | #endif // GTM_OAUTH2_USE_FRAMEWORK_IMPORTS 62 | #endif // GTM_USE_SESSION_FETCHER 63 | 64 | #define GTMOAuth2Fetcher GTMBridgeFetcher 65 | #define GTMOAuth2FetcherService GTMBridgeFetcherService 66 | #define GTMOAuth2FetcherServiceProtocol GTMBridgeFetcherServiceProtocol 67 | #define GTMOAuth2AssertValidSelector GTMBridgeAssertValidSelector 68 | #define GTMOAuth2CookieStorage GTMBridgeCookieStorage 69 | #define kGTMOAuth2FetcherStatusDomain kGTMBridgeFetcherStatusDomain 70 | #define kGTMOAuth2StatusBadRequest kGTMBridgeFetcherStatusBadRequest 71 | 72 | 73 | // Until all OAuth 2 providers are up to the same spec, we'll provide a crude 74 | // way here to override the "Bearer" string in the Authorization header 75 | #ifndef GTM_OAUTH2_BEARER 76 | #define GTM_OAUTH2_BEARER "Bearer" 77 | #endif 78 | 79 | #ifdef __cplusplus 80 | extern "C" { 81 | #endif 82 | 83 | // Service provider name allows stored authorization to be associated with 84 | // the authorizing service 85 | extern NSString *const kGTMOAuth2ServiceProviderGoogle; 86 | 87 | // 88 | // GTMOAuth2SignIn constants, included here for use by clients 89 | // 90 | extern NSString *const kGTMOAuth2ErrorDomain; 91 | 92 | // Error userInfo keys 93 | extern NSString *const kGTMOAuth2ErrorMessageKey; 94 | extern NSString *const kGTMOAuth2ErrorRequestKey; 95 | extern NSString *const kGTMOAuth2ErrorJSONKey; 96 | 97 | typedef NS_ENUM(NSInteger, GTMOAuth2Error) { 98 | // Error code indicating that the window was prematurely closed 99 | GTMOAuth2ErrorWindowClosed = -1000, 100 | GTMOAuth2ErrorAuthorizationFailed = -1001, 101 | GTMOAuth2ErrorTokenExpired = -1002, 102 | GTMOAuth2ErrorTokenUnavailable = -1003, 103 | GTMOAuth2ErrorUnauthorizableRequest = -1004 104 | }; 105 | 106 | #if !GTMOAUTH2AUTHENTICATION_DEPRECATE_OLD_ENUMS 107 | #define kGTMOAuth2ErrorWindowClosed GTMOAuth2ErrorWindowClosed 108 | #define kGTMOAuth2ErrorAuthorizationFailed GTMOAuth2ErrorAuthorizationFailed 109 | #define kGTMOAuth2ErrorTokenExpired GTMOAuth2ErrorTokenExpired 110 | #define kGTMOAuth2ErrorTokenUnavailable GTMOAuth2ErrorTokenUnavailable 111 | #define kGTMOAuth2ErrorUnauthorizableRequest GTMOAuth2ErrorUnauthorizableRequest 112 | #endif 113 | 114 | // Notifications for token fetches 115 | extern NSString *const kGTMOAuth2FetchStarted; 116 | extern NSString *const kGTMOAuth2FetchStopped; 117 | 118 | extern NSString *const kGTMOAuth2FetcherKey; 119 | extern NSString *const kGTMOAuth2FetchTypeKey; 120 | extern NSString *const kGTMOAuth2FetchTypeToken; 121 | extern NSString *const kGTMOAuth2FetchTypeRefresh; 122 | extern NSString *const kGTMOAuth2FetchTypeAssertion; 123 | extern NSString *const kGTMOAuth2FetchTypeUserInfo; 124 | 125 | // Token-issuance errors 126 | extern NSString *const kGTMOAuth2ErrorKey; 127 | extern NSString *const kGTMOAuth2ErrorObjectKey; 128 | 129 | extern NSString *const kGTMOAuth2ErrorInvalidRequest; 130 | extern NSString *const kGTMOAuth2ErrorInvalidClient; 131 | extern NSString *const kGTMOAuth2ErrorInvalidGrant; 132 | extern NSString *const kGTMOAuth2ErrorUnauthorizedClient; 133 | extern NSString *const kGTMOAuth2ErrorUnsupportedGrantType; 134 | extern NSString *const kGTMOAuth2ErrorInvalidScope; 135 | 136 | // Notification that sign-in has completed, and token fetches will begin (useful 137 | // for displaying interstitial messages after the window has closed) 138 | extern NSString *const kGTMOAuth2UserSignedIn; 139 | 140 | // Notification for token changes 141 | extern NSString *const kGTMOAuth2AccessTokenRefreshed; 142 | extern NSString *const kGTMOAuth2RefreshTokenChanged; 143 | extern NSString *const kGTMOAuth2AccessTokenRefreshFailed; 144 | 145 | // Notification for WebView loading 146 | extern NSString *const kGTMOAuth2WebViewStartedLoading; 147 | extern NSString *const kGTMOAuth2WebViewStoppedLoading; 148 | extern NSString *const kGTMOAuth2WebViewKey; 149 | extern NSString *const kGTMOAuth2WebViewStopKindKey; 150 | extern NSString *const kGTMOAuth2WebViewFinished; 151 | extern NSString *const kGTMOAuth2WebViewFailed; 152 | extern NSString *const kGTMOAuth2WebViewCancelled; 153 | 154 | // Notification for network loss during html sign-in display 155 | extern NSString *const kGTMOAuth2NetworkLost; 156 | extern NSString *const kGTMOAuth2NetworkFound; 157 | 158 | #ifdef __cplusplus 159 | } 160 | #endif 161 | 162 | @interface GTMOAuth2Authentication : NSObject { 163 | @private 164 | NSString *clientID_; 165 | NSString *clientSecret_; 166 | NSString *redirectURI_; 167 | NSMutableDictionary *parameters_; 168 | 169 | // authorization parameters 170 | NSURL *tokenURL_; 171 | NSDate *expirationDate_; 172 | 173 | NSString *authorizationTokenKey_; 174 | 175 | NSDictionary *additionalTokenRequestParameters_; 176 | NSDictionary *additionalGrantTypeRequestParameters_; 177 | 178 | // queue of requests for authorization waiting for a valid access token 179 | GTMOAuth2Fetcher *refreshFetcher_; 180 | NSMutableArray *authorizationQueue_; 181 | 182 | id fetcherService_; // WEAK 183 | 184 | BOOL shouldAuthorizeAllRequests_; 185 | 186 | // arbitrary data retained for the user 187 | id userData_; 188 | NSMutableDictionary *properties_; 189 | } 190 | 191 | // OAuth2 standard protocol parameters 192 | // 193 | // These should be the plain strings; any needed escaping will be provided by 194 | // the library. 195 | 196 | // Request properties 197 | @property (atomic, copy) NSString *clientID; 198 | @property (atomic, copy) NSString *clientSecret; 199 | @property (atomic, copy) NSString *redirectURI; 200 | @property (atomic, retain) NSString *scope; 201 | @property (atomic, retain) NSString *tokenType; 202 | @property (atomic, retain) NSString *assertion; 203 | @property (atomic, retain) NSString *refreshScope; 204 | 205 | // Apps may optionally add parameters here to be provided to the token 206 | // endpoint on token requests and refreshes. 207 | @property (atomic, retain) NSDictionary *additionalTokenRequestParameters; 208 | 209 | // Apps may optionally add parameters here to be provided to the token 210 | // endpoint on specific token requests and refreshes, keyed by the grant_type. 211 | // For example, if a different "type" parameter is required for obtaining 212 | // the auth code and on refresh, this might be: 213 | // 214 | // viewController.authentication.additionalGrantTypeRequestParameters = @{ 215 | // @"authorization_code" : @{ @"type" : @"code" }, 216 | // @"refresh_token" : @{ @"type" : @"refresh" } 217 | // }; 218 | @property (atomic, retain) NSDictionary *additionalGrantTypeRequestParameters; 219 | 220 | // Response properties 221 | 222 | // Dictionary of response and other properties; not KVO compliant 223 | @property (atomic, readonly) NSDictionary *parameters; 224 | 225 | @property (atomic, retain) NSString *accessToken; 226 | @property (atomic, retain) NSString *refreshToken; 227 | @property (atomic, retain) NSNumber *expiresIn; 228 | @property (atomic, retain) NSString *code; 229 | @property (atomic, retain) NSString *errorString; 230 | 231 | // URL for obtaining access tokens 232 | @property (atomic, copy) NSURL *tokenURL; 233 | 234 | // Calculated expiration date (expiresIn seconds added to the 235 | // time the access token was received.) 236 | @property (atomic, copy) NSDate *expirationDate; 237 | 238 | // Service identifier, like "Google"; not used for authentication 239 | // 240 | // The provider name is just for allowing stored authorization to be associated 241 | // with the authorizing service. 242 | @property (atomic, copy) NSString *serviceProvider; 243 | 244 | // User ID; not used for authentication 245 | @property (retain) NSString *userID; 246 | 247 | // User email and verified status; not used for authentication 248 | // 249 | // The verified string can be checked with -boolValue. If the result is false, 250 | // then the email address is listed with the account on the server, but the 251 | // address has not been confirmed as belonging to the owner of the account. 252 | @property (atomic, retain) NSString *userEmail; 253 | @property (atomic, retain) NSString *userEmailIsVerified; 254 | 255 | // Property indicating if this auth has a refresh or access token so is suitable 256 | // for authorizing a request. This does not guarantee that the token is valid. 257 | @property (atomic, readonly) BOOL canAuthorize; 258 | 259 | // Property indicating if this object will authorize plain http request 260 | // (as well as any non-https requests.) Default is NO, only requests with the 261 | // scheme https are authorized, since security may be compromised if tokens 262 | // are sent over the wire using an unencrypted protocol like http. 263 | @property (atomic, assign) BOOL shouldAuthorizeAllRequests; 264 | 265 | // userData is retained for the convenience of the caller 266 | @property (atomic, retain) id userData; 267 | 268 | // Stored property values are retained for the convenience of the caller 269 | @property (atomic, retain) NSDictionary *properties; 270 | 271 | // Property for the optional fetcher service instance to be used to create 272 | // fetchers 273 | // 274 | // Fetcher service objects retain authorizations, so this is weak to avoid 275 | // circular retains. 276 | @property (atomic, assign) id fetcherService; // WEAK 277 | 278 | // Key for the response parameter used for the authorization header; by default, 279 | // "access_token" is used, but some servers may expect alternatives, like 280 | // "id_token". 281 | @property (atomic, copy) NSString *authorizationTokenKey; 282 | 283 | // Convenience method for creating an authentication object 284 | + (id)authenticationWithServiceProvider:(NSString *)serviceProvider 285 | tokenURL:(NSURL *)tokenURL 286 | redirectURI:(NSString *)redirectURI 287 | clientID:(NSString *)clientID 288 | clientSecret:(NSString *)clientSecret; 289 | 290 | // Clear out any authentication values, prepare for a new request fetch 291 | - (void)reset; 292 | 293 | // Main authorization entry points 294 | // 295 | // These will refresh the access token, if necessary, add the access token to 296 | // the request, then invoke the callback. 297 | // 298 | // The request argument may be nil to just force a refresh of the access token, 299 | // if needed. 300 | // 301 | // NOTE: To avoid accidental leaks of bearer tokens, the request must 302 | // be for a URL with the scheme https unless the shouldAuthorizeAllRequests 303 | // property is set. 304 | 305 | // The finish selector should have a signature matching 306 | // - (void)authentication:(GTMOAuth2Authentication *)auth 307 | // request:(NSMutableURLRequest *)request 308 | // finishedWithError:(NSError *)error; 309 | 310 | - (void)authorizeRequest:(NSMutableURLRequest *)request 311 | delegate:(id)delegate 312 | didFinishSelector:(SEL)sel; 313 | 314 | #if NS_BLOCKS_AVAILABLE 315 | - (void)authorizeRequest:(NSMutableURLRequest *)request 316 | completionHandler:(void (^)(NSError *error))handler; 317 | #endif 318 | 319 | // Synchronous entry point; authorizing this way cannot refresh an expired 320 | // access token 321 | - (BOOL)authorizeRequest:(NSMutableURLRequest *)request; 322 | 323 | // If the authentication is waiting for a refresh to complete, spin the run 324 | // loop, discarding events, until the fetch has completed 325 | // 326 | // This is only for use in testing or in tools without a user interface. 327 | - (void)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds; 328 | 329 | 330 | ////////////////////////////////////////////////////////////////////////////// 331 | // 332 | // Internal properties and methods for use by GTMOAuth2SignIn 333 | // 334 | 335 | // Pending fetcher to get a new access token, if any 336 | @property (atomic, retain) GTMOAuth2Fetcher *refreshFetcher; 337 | 338 | // Check if a request is queued up to be authorized 339 | - (BOOL)isAuthorizingRequest:(NSURLRequest *)request; 340 | 341 | // Check if a request appears to be authorized 342 | - (BOOL)isAuthorizedRequest:(NSURLRequest *)request; 343 | 344 | // Stop any pending refresh fetch. This will also cancel the authorization 345 | // for all fetch requests pending authorization. 346 | - (void)stopAuthorization; 347 | 348 | // Prevents authorization callback for a given request. 349 | - (void)stopAuthorizationForRequest:(NSURLRequest *)request; 350 | 351 | // OAuth fetch user-agent header value 352 | - (NSString *)userAgent; 353 | 354 | // Parse and set token and token secret from response data 355 | - (void)setKeysForResponseString:(NSString *)str; 356 | - (void)setKeysForResponseDictionary:(NSDictionary *)dict; 357 | 358 | // Persistent token string for keychain storage 359 | // 360 | // We'll use the format "refresh_token=foo&serviceProvider=bar" so we can 361 | // easily alter what portions of the auth data are stored 362 | // 363 | // Use these methods for serialization 364 | - (NSString *)persistenceResponseString; 365 | - (void)setKeysForPersistenceResponseString:(NSString *)str; 366 | 367 | // method to begin fetching an access token, used by the sign-in object 368 | - (GTMOAuth2Fetcher *)beginTokenFetchWithDelegate:(id)delegate 369 | didFinishSelector:(SEL)finishedSel; 370 | 371 | // Entry point to post a notification about a fetcher currently used for 372 | // obtaining or refreshing a token; the sign-in object will also use this 373 | // to indicate when the user's email address is being fetched. 374 | // 375 | // Fetch type constants are above under "notifications for token fetches" 376 | - (void)notifyFetchIsRunning:(BOOL)isStarting 377 | fetcher:(GTMOAuth2Fetcher *)fetcher 378 | type:(NSString *)fetchType; 379 | 380 | // Arbitrary key-value properties retained for the user 381 | - (void)setProperty:(id)obj forKey:(NSString *)key; 382 | - (id)propertyForKey:(NSString *)key; 383 | 384 | // 385 | // Utilities 386 | // 387 | 388 | + (NSString *)encodedOAuthValueForString:(NSString *)str; 389 | 390 | + (NSString *)encodedQueryParametersForDictionary:(NSDictionary *)dict; 391 | 392 | + (NSDictionary *)dictionaryWithResponseString:(NSString *)responseStr; 393 | 394 | + (NSDictionary *)dictionaryWithJSONData:(NSData *)data; 395 | 396 | + (NSString *)scopeWithStrings:(NSString *)firstStr, ... NS_REQUIRES_NIL_TERMINATION; 397 | @end 398 | 399 | #endif // GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 400 | -------------------------------------------------------------------------------- /Source/GTMOAuth2SignIn.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // 17 | // This sign-in object opens and closes the web view window as needed for 18 | // users to sign in. For signing in to Google, it also obtains 19 | // the authenticated user's email address. 20 | // 21 | // Typically, this will be managed for the application by 22 | // GTMOAuth2ViewControllerTouch or GTMOAuth2WindowController, so this 23 | // class's interface is interesting only if 24 | // you are creating your own window controller for sign-in. 25 | // 26 | // 27 | // Delegate methods implemented by the window controller 28 | // 29 | // The window controller implements two methods for use by the sign-in object, 30 | // the webRequestSelector and the finishedSelector: 31 | // 32 | // webRequestSelector has a signature matching 33 | // - (void)signIn:(GTMOAuth2SignIn *)signIn displayRequest:(NSURLRequest *)request 34 | // 35 | // The web request selector will be invoked with a request to be displayed, or 36 | // nil to close the window when the final callback request has been encountered. 37 | // 38 | // 39 | // finishedSelector has a signature matching 40 | // - (void)signin:(GTMOAuth2SignIn *)signin finishedWithAuth:(GTMOAuth2Authentication *)auth error:(NSError *)error 41 | // 42 | // The finished selector will be invoked when sign-in has completed, except 43 | // when explicitly canceled by calling cancelSigningIn 44 | // 45 | 46 | #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 47 | 48 | #import 49 | #import 50 | 51 | #import "GTMOAuth2Authentication.h" 52 | 53 | @interface GTMOAuth2SignIn : NSObject { 54 | @private 55 | GTMOAuth2Authentication *auth_; 56 | 57 | // the endpoint for displaying the sign-in page 58 | NSURL *authorizationURL_; 59 | NSDictionary *additionalAuthorizationParameters_; 60 | 61 | id delegate_; 62 | SEL webRequestSelector_; 63 | SEL finishedSelector_; 64 | 65 | BOOL hasHandledCallback_; 66 | 67 | GTMOAuth2Fetcher *pendingFetcher_; 68 | 69 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 70 | BOOL shouldFetchGoogleUserEmail_; 71 | BOOL shouldFetchGoogleUserProfile_; 72 | NSDictionary *userProfile_; 73 | #endif 74 | 75 | SCNetworkReachabilityRef reachabilityRef_; 76 | NSTimer *networkLossTimer_; 77 | NSTimeInterval networkLossTimeoutInterval_; 78 | BOOL hasNotifiedNetworkLoss_; 79 | 80 | id userData_; 81 | } 82 | 83 | @property (nonatomic, retain) GTMOAuth2Authentication *authentication; 84 | 85 | @property (nonatomic, retain) NSURL *authorizationURL; 86 | @property (nonatomic, retain) NSDictionary *additionalAuthorizationParameters; 87 | 88 | // The delegate is released when signing in finishes or is cancelled 89 | @property (nonatomic, retain) id delegate; 90 | @property (nonatomic, assign) SEL webRequestSelector; 91 | @property (nonatomic, assign) SEL finishedSelector; 92 | 93 | @property (nonatomic, retain) id userData; 94 | 95 | // By default, signing in to Google will fetch the user's email, but will not 96 | // fetch the user's profile. 97 | // 98 | // The email is saved in the auth object. 99 | // The profile is available immediately after sign-in. 100 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 101 | @property (nonatomic, assign) BOOL shouldFetchGoogleUserEmail; 102 | @property (nonatomic, assign) BOOL shouldFetchGoogleUserProfile; 103 | @property (nonatomic, retain, readonly) NSDictionary *userProfile; 104 | #endif 105 | 106 | // The default timeout for an unreachable network during display of the 107 | // sign-in page is 30 seconds; set this to 0 to have no timeout 108 | @property (nonatomic, assign) NSTimeInterval networkLossTimeoutInterval; 109 | 110 | // The delegate is retained until sign-in has completed or been canceled 111 | // 112 | // designated initializer 113 | - (id)initWithAuthentication:(GTMOAuth2Authentication *)auth 114 | authorizationURL:(NSURL *)authorizationURL 115 | delegate:(id)delegate 116 | webRequestSelector:(SEL)webRequestSelector 117 | finishedSelector:(SEL)finishedSelector; 118 | 119 | // A default authentication object for signing in to Google services 120 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 121 | + (GTMOAuth2Authentication *)standardGoogleAuthenticationForScope:(NSString *)scope 122 | clientID:(NSString *)clientID 123 | clientSecret:(NSString *)clientSecret; 124 | #endif 125 | 126 | #pragma mark Methods used by the Window Controller 127 | 128 | // Start the sequence of fetches and sign-in window display for sign-in 129 | - (BOOL)startSigningIn; 130 | 131 | // Stop any pending fetches, and close the window (but don't call the 132 | // delegate's finishedSelector) 133 | - (void)cancelSigningIn; 134 | 135 | // Window controllers must tell the sign-in object about any redirect 136 | // requested by the web view, and any changes in the webview window title 137 | // 138 | // If these return YES then the event was handled by the 139 | // sign-in object (typically by closing the window) and should be ignored by 140 | // the window controller's web view 141 | 142 | - (BOOL)requestRedirectedToRequest:(NSURLRequest *)redirectedRequest; 143 | - (BOOL)titleChanged:(NSString *)title; 144 | - (BOOL)cookiesChanged:(NSHTTPCookieStorage *)cookieStorage; 145 | - (BOOL)loadFailedWithError:(NSError *)error; 146 | 147 | // Window controllers must tell the sign-in object if the window was closed 148 | // prematurely by the user (but not by the sign-in object); this calls the 149 | // delegate's finishedSelector 150 | - (void)windowWasClosed; 151 | 152 | // Start the sequences for signing in with an authorization code. The 153 | // authentication must contain an authorization code, otherwise the process 154 | // will fail. 155 | - (void)authCodeObtained; 156 | 157 | #pragma mark - 158 | 159 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 160 | // Revocation of an authorized token from Google 161 | + (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth; 162 | 163 | // Create a fetcher for obtaining the user's Google email address or profile, 164 | // according to the current auth scopes. 165 | // 166 | // The auth object must have been created with appropriate scopes. 167 | // 168 | // The fetcher's response data can be parsed with NSJSONSerialization. 169 | + (GTMOAuth2Fetcher *)userInfoFetcherWithAuth:(GTMOAuth2Authentication *)auth; 170 | 171 | // Decode a web-safe Base64 encoded string. 172 | + (NSData *)decodeWebSafeBase64:(NSString *)base64Str; 173 | #endif 174 | 175 | #pragma mark - 176 | 177 | // Standard authentication values 178 | + (NSString *)nativeClientRedirectURI; 179 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 180 | + (NSURL *)googleAuthorizationURL; 181 | + (NSURL *)googleTokenURL; 182 | + (NSURL *)googleUserInfoURL; 183 | #endif 184 | 185 | @end 186 | 187 | #endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 188 | -------------------------------------------------------------------------------- /Source/Mac/GTMOAuth2Framework-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.google.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | FMWK 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Source/Mac/GTMOAuth2WindowController.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // GTMOAuth2WindowController 17 | // 18 | // This window controller for Mac handles sign-in via OAuth2 to Google or 19 | // other services. 20 | // 21 | // This controller is not reusable; create a new instance of this controller 22 | // every time the user will sign in. 23 | // 24 | // Sample usage for signing in to a Google service: 25 | // 26 | // static NSString *const kKeychainItemName = @”My App: Google Plus”; 27 | // NSString *scope = @"https://www.googleapis.com/auth/plus.me"; 28 | // 29 | // 30 | // GTMOAuth2WindowController *windowController; 31 | // windowController = [[[GTMOAuth2WindowController alloc] initWithScope:scope 32 | // clientID:clientID 33 | // clientSecret:clientSecret 34 | // keychainItemName:kKeychainItemName 35 | // resourceBundle:nil] autorelease]; 36 | // 37 | // [windowController signInSheetModalForWindow:mMainWindow 38 | // delegate:self 39 | // finishedSelector:@selector(windowController:finishedWithAuth:error:)]; 40 | // 41 | // The finished selector should have a signature matching this: 42 | // 43 | // - (void)windowController:(GTMOAuth2WindowController *)windowController 44 | // finishedWithAuth:(GTMOAuth2Authentication *)auth 45 | // error:(NSError *)error { 46 | // if (error != nil) { 47 | // // sign in failed 48 | // } else { 49 | // // sign in succeeded 50 | // // 51 | // // with the GTL library, pass the authentication to the service object, 52 | // // like 53 | // // [[self contactService] setAuthorizer:auth]; 54 | // // 55 | // // or use it to sign a request directly, like 56 | // // BOOL isAuthorizing = [self authorizeRequest:request 57 | // // delegate:self 58 | // // didFinishSelector:@selector(auth:finishedWithError:)]; 59 | // } 60 | // } 61 | // 62 | // To sign in to services other than Google, use the longer init method, 63 | // as shown in the sample application 64 | // 65 | // If the network connection is lost for more than 30 seconds while the sign-in 66 | // html is displayed, the notification kGTLOAuthNetworkLost will be sent. 67 | 68 | #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 69 | 70 | #import 71 | 72 | #if !TARGET_OS_IPHONE 73 | 74 | #import 75 | #import 76 | 77 | #import "GTMOAuth2SignIn.h" 78 | #import "GTMOAuth2Authentication.h" 79 | 80 | #if !GTM_USE_SESSION_FETCHER 81 | #import "GTMHTTPFetchHistory.h" // for GTMCookieStorage 82 | #endif 83 | 84 | @class GTMOAuth2SignIn; 85 | 86 | @interface GTMOAuth2WindowController : NSWindowController { 88 | @private 89 | // IBOutlets 90 | NSButton *keychainCheckbox_; 91 | WebView *webView_; 92 | NSButton *webCloseButton_; 93 | NSButton *webBackButton_; 94 | 95 | // the object responsible for the sign-in networking sequence; it holds 96 | // onto the authentication object as well 97 | GTMOAuth2SignIn *signIn_; 98 | 99 | // the page request to load when awakeFromNib occurs 100 | NSURLRequest *initialRequest_; 101 | 102 | // local storage for WebKit cookies so they're not shared with Safari 103 | GTMOAuth2CookieStorage *cookieStorage_; 104 | 105 | // the user we're calling back 106 | // 107 | // the delegate is retained only until the callback is invoked 108 | // or the sign-in is canceled 109 | id delegate_; 110 | SEL finishedSelector_; 111 | 112 | #if NS_BLOCKS_AVAILABLE 113 | void (^completionBlock_)(GTMOAuth2Authentication *, NSError *); 114 | #elif !__LP64__ 115 | // placeholders: for 32-bit builds, keep the size of the object's ivar section 116 | // the same with and without blocks 117 | #ifndef __clang_analyzer__ 118 | id completionPlaceholder_; 119 | #endif 120 | #endif 121 | 122 | // flag allowing application to quit during display of sign-in sheet on 10.6 123 | // and later 124 | BOOL shouldAllowApplicationTermination_; 125 | 126 | // delegate method for handling URLs to be opened in external windows 127 | SEL externalRequestSelector_; 128 | 129 | BOOL isWindowShown_; 130 | 131 | // paranoid flag to ensure we only close once during the sign-in sequence 132 | BOOL hasDoneFinalRedirect_; 133 | 134 | // paranoid flag to ensure we only call the user back once 135 | BOOL hasCalledFinished_; 136 | 137 | // if non-nil, we display as a sheet on the specified window 138 | NSWindow *sheetModalForWindow_; 139 | 140 | // if non-empty, the name of the application and service used for the 141 | // keychain item 142 | NSString *keychainItemName_; 143 | 144 | // if non-nil, the html string to be displayed immediately upon opening 145 | // of the web view 146 | NSString *initialHTMLString_; 147 | 148 | // if true, we allow default WebView handling of cookies, so the 149 | // same user remains signed in each time the dialog is displayed 150 | BOOL shouldPersistUser_; 151 | 152 | // user-defined data 153 | id userData_; 154 | NSMutableDictionary *properties_; 155 | } 156 | 157 | // User interface elements 158 | @property (nonatomic, assign) IBOutlet NSButton *keychainCheckbox; 159 | @property (nonatomic, assign) IBOutlet WebView *webView; 160 | @property (nonatomic, assign) IBOutlet NSButton *webCloseButton; 161 | @property (nonatomic, assign) IBOutlet NSButton *webBackButton; 162 | 163 | // The application and service name to use for saving the auth tokens 164 | // to the keychain 165 | @property (nonatomic, copy) NSString *keychainItemName; 166 | 167 | // If true, the sign-in will remember which user was last signed in 168 | // 169 | // Defaults to false, so showing the sign-in window will always ask for 170 | // the username and password, rather than skip to the grant authorization 171 | // page. During development, it may be convenient to set this to true 172 | // to speed up signing in. 173 | @property (nonatomic, assign) BOOL shouldPersistUser; 174 | 175 | // Optional html string displayed immediately upon opening the web view 176 | // 177 | // This string is visible just until the sign-in web page loads, and 178 | // may be used for a "Loading..." type of message 179 | @property (nonatomic, copy) NSString *initialHTMLString; 180 | 181 | // The default timeout for an unreachable network during display of the 182 | // sign-in page is 30 seconds, after which the notification 183 | // kGTLOAuthNetworkLost is sent; set this to 0 to have no timeout 184 | @property (nonatomic, assign) NSTimeInterval networkLossTimeoutInterval; 185 | 186 | // On 10.6 and later, the sheet can allow application termination by calling 187 | // NSWindow's setPreventsApplicationTerminationWhenModal: 188 | @property (nonatomic, assign) BOOL shouldAllowApplicationTermination; 189 | 190 | // Selector for a delegate method to handle requests sent to an external 191 | // browser. 192 | // 193 | // Selector should have a signature matching 194 | // - (void)windowController:(GTMOAuth2WindowController *)controller 195 | // opensRequest:(NSURLRequest *)request; 196 | // 197 | // The controller's default behavior is to use NSWorkspace's openURL: 198 | @property (nonatomic, assign) SEL externalRequestSelector; 199 | 200 | // The underlying object to hold authentication tokens and authorize http 201 | // requests 202 | @property (nonatomic, retain, readonly) GTMOAuth2Authentication *authentication; 203 | 204 | // The underlying object which performs the sign-in networking sequence 205 | @property (nonatomic, retain, readonly) GTMOAuth2SignIn *signIn; 206 | 207 | // Any arbitrary data object the user would like the controller to retain 208 | @property (nonatomic, retain) id userData; 209 | 210 | // Stored property values are retained for the convenience of the caller 211 | - (void)setProperty:(id)obj forKey:(NSString *)key; 212 | - (id)propertyForKey:(NSString *)key; 213 | 214 | @property (nonatomic, retain) NSDictionary *properties; 215 | 216 | - (IBAction)closeWindow:(id)sender; 217 | 218 | // Create a controller for authenticating to Google services 219 | // 220 | // scope is the requested scope of authorization 221 | // (like "http://www.google.com/m8/feeds") 222 | // 223 | // keychainItemName is used for storing the token on the keychain, 224 | // and is required for the "remember for later" checkbox to be shown; 225 | // keychainItemName should be like "My Application: Google Contacts" 226 | // (or set to nil if no persistent keychain storage is desired) 227 | // 228 | // resourceBundle may be nil if the window is in the main bundle's nib 229 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 230 | + (id)controllerWithScope:(NSString *)scope 231 | clientID:(NSString *)clientID 232 | clientSecret:(NSString *)clientSecret 233 | keychainItemName:(NSString *)keychainItemName // may be nil 234 | resourceBundle:(NSBundle *)bundle; // may be nil 235 | 236 | - (id)initWithScope:(NSString *)scope 237 | clientID:(NSString *)clientID 238 | clientSecret:(NSString *)clientSecret 239 | keychainItemName:(NSString *)keychainItemName 240 | resourceBundle:(NSBundle *)bundle; 241 | #endif 242 | 243 | // Create a controller for authenticating to non-Google services, taking 244 | // explicit endpoint URLs and an authentication object 245 | + (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth 246 | authorizationURL:(NSURL *)authorizationURL 247 | keychainItemName:(NSString *)keychainItemName // may be nil 248 | resourceBundle:(NSBundle *)bundle; // may be nil 249 | 250 | // This is the designated initializer 251 | - (id)initWithAuthentication:(GTMOAuth2Authentication *)auth 252 | authorizationURL:(NSURL *)authorizationURL 253 | keychainItemName:(NSString *)keychainItemName 254 | resourceBundle:(NSBundle *)bundle; 255 | 256 | // Entry point to begin displaying the sign-in window 257 | // 258 | // the finished selector should have a signature matching 259 | // - (void)windowController:(GTMOAuth2WindowController *)windowController 260 | // finishedWithAuth:(GTMOAuth2Authentication *)auth 261 | // error:(NSError *)error { 262 | // 263 | // Once the finished method has been invoked with no error, the auth object 264 | // may be used to authorize requests (refreshing the access token, if necessary, 265 | // and adding the auth header) like: 266 | // 267 | // [authorizer authorizeRequest:myNSMutableURLRequest] 268 | // delegate:self 269 | // didFinishSelector:@selector(auth:finishedWithError:)]; 270 | // 271 | // or can be stored in a GTL service object like 272 | // GTLServiceGoogleContact *service = [self contactService]; 273 | // [service setAuthorizer:auth]; 274 | // 275 | // The delegate is retained only until the finished selector is invoked or 276 | // the sign-in is canceled 277 | - (void)signInSheetModalForWindow:(NSWindow *)parentWindowOrNil 278 | delegate:(id)delegate 279 | finishedSelector:(SEL)finishedSelector; 280 | 281 | #if NS_BLOCKS_AVAILABLE 282 | - (void)signInSheetModalForWindow:(NSWindow *)parentWindowOrNil 283 | completionHandler:(void (^)(GTMOAuth2Authentication *auth, NSError *error))handler; 284 | #endif 285 | 286 | - (void)cancelSigningIn; 287 | 288 | // Subclasses may override authNibName to specify a custom name 289 | + (NSString *)authNibName; 290 | 291 | // apps may replace the sign-in class with their own subclass of it 292 | + (Class)signInClass; 293 | + (void)setSignInClass:(Class)theClass; 294 | 295 | // Revocation of an authorized token from Google 296 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 297 | + (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth; 298 | #endif 299 | 300 | // Keychain 301 | // 302 | // The keychain checkbox is shown if the keychain application service 303 | // name (typically set in the initWithScope: method) is non-empty 304 | // 305 | 306 | // Create an authentication object for Google services from the access 307 | // token and secret stored in the keychain; if no token is available, return 308 | // an unauthorized auth object 309 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 310 | + (GTMOAuth2Authentication *)authForGoogleFromKeychainForName:(NSString *)keychainItemName 311 | clientID:(NSString *)clientID 312 | clientSecret:(NSString *)clientSecret; 313 | #endif 314 | 315 | // Add tokens from the keychain, if available, to the authentication object 316 | // 317 | // returns YES if the authentication object was authorized from the keychain 318 | + (BOOL)authorizeFromKeychainForName:(NSString *)keychainItemName 319 | authentication:(GTMOAuth2Authentication *)auth; 320 | 321 | // Method for deleting the stored access token and secret, useful for "signing 322 | // out" 323 | + (BOOL)removeAuthFromKeychainForName:(NSString *)keychainItemName; 324 | 325 | // Method for saving the stored access token and secret; typically, this method 326 | // is used only by the window controller 327 | + (BOOL)saveAuthToKeychainForName:(NSString *)keychainItemName 328 | authentication:(GTMOAuth2Authentication *)auth; 329 | @end 330 | 331 | #endif // #if !TARGET_OS_IPHONE 332 | 333 | #endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 334 | -------------------------------------------------------------------------------- /Source/Mac/GTMOAuth2WindowController.m: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #import 17 | 18 | #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 19 | 20 | #if !TARGET_OS_IPHONE 21 | 22 | #ifndef GTM_USE_BEGIN_SHEET 23 | #if (defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9) 24 | #define GTM_USE_BEGIN_SHEET 1 25 | #endif 26 | #endif 27 | 28 | 29 | #import "GTMOAuth2WindowController.h" 30 | 31 | @interface GTMOAuth2WindowController () 32 | @property (nonatomic, retain) GTMOAuth2SignIn *signIn; 33 | @property (nonatomic, copy) NSURLRequest *initialRequest; 34 | @property (nonatomic, retain) GTMOAuth2CookieStorage *cookieStorage; 35 | @property (nonatomic, retain) NSWindow *sheetModalForWindow; 36 | 37 | - (void)signInCommonForWindow:(NSWindow *)parentWindowOrNil; 38 | - (void)setupSheetTerminationHandling; 39 | - (void)destroyWindow; 40 | - (void)handlePrematureWindowClose; 41 | - (BOOL)shouldUseKeychain; 42 | - (void)signIn:(GTMOAuth2SignIn *)signIn displayRequest:(NSURLRequest *)request; 43 | - (void)signIn:(GTMOAuth2SignIn *)signIn finishedWithAuth:(GTMOAuth2Authentication *)auth error:(NSError *)error; 44 | 45 | - (void)handleCookiesForResponse:(NSURLResponse *)response; 46 | - (NSURLRequest *)addCookiesToRequest:(NSURLRequest *)request; 47 | @end 48 | 49 | static const char *kKeychainAccountName = "OAuth"; 50 | 51 | @implementation GTMOAuth2WindowController 52 | 53 | // IBOutlets 54 | @synthesize keychainCheckbox = keychainCheckbox_, 55 | webView = webView_, 56 | webCloseButton = webCloseButton_, 57 | webBackButton = webBackButton_; 58 | 59 | // regular ivars 60 | @synthesize signIn = signIn_, 61 | initialRequest = initialRequest_, 62 | cookieStorage = cookieStorage_, 63 | sheetModalForWindow = sheetModalForWindow_, 64 | keychainItemName = keychainItemName_, 65 | initialHTMLString = initialHTMLString_, 66 | shouldAllowApplicationTermination = shouldAllowApplicationTermination_, 67 | externalRequestSelector = externalRequestSelector_, 68 | shouldPersistUser = shouldPersistUser_, 69 | userData = userData_, 70 | properties = properties_; 71 | 72 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 73 | // Create a controller for authenticating to Google services 74 | + (id)controllerWithScope:(NSString *)scope 75 | clientID:(NSString *)clientID 76 | clientSecret:(NSString *)clientSecret 77 | keychainItemName:(NSString *)keychainItemName 78 | resourceBundle:(NSBundle *)bundle { 79 | return [[[self alloc] initWithScope:scope 80 | clientID:clientID 81 | clientSecret:clientSecret 82 | keychainItemName:keychainItemName 83 | resourceBundle:bundle] autorelease]; 84 | } 85 | 86 | - (id)initWithScope:(NSString *)scope 87 | clientID:(NSString *)clientID 88 | clientSecret:(NSString *)clientSecret 89 | keychainItemName:(NSString *)keychainItemName 90 | resourceBundle:(NSBundle *)bundle { 91 | Class signInClass = [[self class] signInClass]; 92 | GTMOAuth2Authentication *auth; 93 | auth = [signInClass standardGoogleAuthenticationForScope:scope 94 | clientID:clientID 95 | clientSecret:clientSecret]; 96 | NSURL *authorizationURL = [signInClass googleAuthorizationURL]; 97 | return [self initWithAuthentication:auth 98 | authorizationURL:authorizationURL 99 | keychainItemName:keychainItemName 100 | resourceBundle:bundle]; 101 | } 102 | #endif 103 | 104 | // Create a controller for authenticating to any service 105 | + (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth 106 | authorizationURL:(NSURL *)authorizationURL 107 | keychainItemName:(NSString *)keychainItemName 108 | resourceBundle:(NSBundle *)bundle { 109 | return [[[self alloc] initWithAuthentication:auth 110 | authorizationURL:authorizationURL 111 | keychainItemName:keychainItemName 112 | resourceBundle:bundle] autorelease]; 113 | } 114 | 115 | - (id)initWithAuthentication:(GTMOAuth2Authentication *)auth 116 | authorizationURL:(NSURL *)authorizationURL 117 | keychainItemName:(NSString *)keychainItemName 118 | resourceBundle:(NSBundle *)bundle { 119 | if (bundle == nil) { 120 | bundle = [NSBundle mainBundle]; 121 | } 122 | 123 | NSString *nibName = [[self class] authNibName]; 124 | NSString *nibPath = [bundle pathForResource:nibName 125 | ofType:@"nib"]; 126 | 127 | self = [super initWithWindowNibPath:nibPath 128 | owner:self]; 129 | if (self != nil) { 130 | // use the supplied auth and OAuth endpoint URLs 131 | Class signInClass = [[self class] signInClass]; 132 | signIn_ = [[signInClass alloc] initWithAuthentication:auth 133 | authorizationURL:authorizationURL 134 | delegate:self 135 | webRequestSelector:@selector(signIn:displayRequest:) 136 | finishedSelector:@selector(signIn:finishedWithAuth:error:)]; 137 | keychainItemName_ = [keychainItemName copy]; 138 | 139 | // create local, temporary storage for WebKit cookies 140 | cookieStorage_ = [[GTMOAuth2CookieStorage alloc] init]; 141 | } 142 | return self; 143 | } 144 | 145 | - (void)dealloc { 146 | // The destroyWindow method below calls the WebView's stopLoading, which should avoid 147 | // any later callbacks, but apparently doesn't accomplish that, as we're seeing calls 148 | // back to the controller after it has dealloc'd. So we'll explicitly set the delegate 149 | // pointers to nil. 150 | [webView_ setResourceLoadDelegate:nil]; 151 | [webView_ setPolicyDelegate:nil]; 152 | 153 | [signIn_ release]; 154 | [initialRequest_ release]; 155 | [cookieStorage_ release]; 156 | [delegate_ release]; 157 | #if NS_BLOCKS_AVAILABLE 158 | [completionBlock_ release]; 159 | #endif 160 | [sheetModalForWindow_ release]; 161 | [keychainItemName_ release]; 162 | [initialHTMLString_ release]; 163 | [userData_ release]; 164 | [properties_ release]; 165 | 166 | [super dealloc]; 167 | } 168 | 169 | - (void)awakeFromNib { 170 | // load the requested initial sign-in page 171 | [self.webView setResourceLoadDelegate:self]; 172 | [self.webView setPolicyDelegate:self]; 173 | 174 | // the app may prefer some html other than blank white to be displayed 175 | // before the sign-in web page loads 176 | NSString *html = self.initialHTMLString; 177 | if ([html length] > 0) { 178 | [[self.webView mainFrame] loadHTMLString:html baseURL:nil]; 179 | } 180 | 181 | // hide the keychain checkbox if we're not supporting keychain 182 | BOOL hideKeychainCheckbox = ![self shouldUseKeychain]; 183 | 184 | const NSTimeInterval kJanuary2011 = 1293840000; 185 | BOOL isDateValid = ([[NSDate date] timeIntervalSince1970] > kJanuary2011); 186 | if (isDateValid) { 187 | // start the asynchronous load of the sign-in web page 188 | [[self.webView mainFrame] performSelector:@selector(loadRequest:) 189 | withObject:self.initialRequest 190 | afterDelay:0.01 191 | inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; 192 | } else { 193 | // clock date is invalid, so signing in would fail with an unhelpful error 194 | // from the server. Warn the user in an html string showing a watch icon, 195 | // question mark, and the system date and time. Hopefully this will clue 196 | // in brighter users, or at least let them make a useful screenshot to show 197 | // to developers. 198 | // 199 | // Even better is for apps to check the system clock and show some more 200 | // helpful, localized instructions for users; this is really a fallback. 201 | NSString *const htmlTemplate = @"
" 202 | @"⌚ ?
System Clock Incorrect
%@" 203 | @"
"; 204 | NSString *errHTML = [NSString stringWithFormat:htmlTemplate, [NSDate date]]; 205 | 206 | [[webView_ mainFrame] loadHTMLString:errHTML baseURL:nil]; 207 | hideKeychainCheckbox = YES; 208 | } 209 | 210 | #if DEBUG 211 | // Verify that Javascript is enabled 212 | BOOL hasJS = [[webView_ preferences] isJavaScriptEnabled]; 213 | NSAssert(hasJS, @"GTMOAuth2: Javascript is required"); 214 | #endif 215 | 216 | [keychainCheckbox_ setHidden:hideKeychainCheckbox]; 217 | } 218 | 219 | + (NSString *)authNibName { 220 | // subclasses may override this to specify a custom nib name 221 | return @"GTMOAuth2Window"; 222 | } 223 | 224 | #pragma mark - 225 | 226 | - (void)signInSheetModalForWindow:(NSWindow *)parentWindowOrNil 227 | delegate:(id)delegate 228 | finishedSelector:(SEL)finishedSelector { 229 | // check the selector on debug builds 230 | GTMOAuth2AssertValidSelector(delegate, finishedSelector, 231 | @encode(GTMOAuth2WindowController *), @encode(GTMOAuth2Authentication *), 232 | @encode(NSError *), 0); 233 | 234 | delegate_ = [delegate retain]; 235 | finishedSelector_ = finishedSelector; 236 | 237 | [self signInCommonForWindow:parentWindowOrNil]; 238 | } 239 | 240 | #if NS_BLOCKS_AVAILABLE 241 | - (void)signInSheetModalForWindow:(NSWindow *)parentWindowOrNil 242 | completionHandler:(void (^)(GTMOAuth2Authentication *, NSError *))handler { 243 | completionBlock_ = [handler copy]; 244 | 245 | [self signInCommonForWindow:parentWindowOrNil]; 246 | } 247 | #endif 248 | 249 | - (void)signInCommonForWindow:(NSWindow *)parentWindowOrNil { 250 | self.sheetModalForWindow = parentWindowOrNil; 251 | hasDoneFinalRedirect_ = NO; 252 | hasCalledFinished_ = NO; 253 | 254 | [self.signIn startSigningIn]; 255 | } 256 | 257 | - (void)cancelSigningIn { 258 | // The user has explicitly asked us to cancel signing in 259 | // (so no further callback is required) 260 | hasCalledFinished_ = YES; 261 | 262 | [delegate_ autorelease]; 263 | delegate_ = nil; 264 | 265 | #if NS_BLOCKS_AVAILABLE 266 | [completionBlock_ autorelease]; 267 | completionBlock_ = nil; 268 | #endif 269 | 270 | // The signIn object's cancel method will close the window 271 | [self.signIn cancelSigningIn]; 272 | hasDoneFinalRedirect_ = YES; 273 | } 274 | 275 | - (IBAction)closeWindow:(id)sender { 276 | // dismiss the window/sheet before we call back the client 277 | [self destroyWindow]; 278 | [self handlePrematureWindowClose]; 279 | } 280 | 281 | #pragma mark SignIn callbacks 282 | 283 | - (void)signIn:(GTMOAuth2SignIn *)signIn displayRequest:(NSURLRequest *)request { 284 | // this is the signIn object's webRequest method, telling the controller 285 | // to either display the request in the webview, or close the window 286 | // 287 | // All web requests and all window closing goes through this routine 288 | 289 | #if DEBUG 290 | if ((isWindowShown_ && request != nil) 291 | || (!isWindowShown_ && request == nil)) { 292 | NSLog(@"Window state unexpected for request %@", [request URL]); 293 | return; 294 | } 295 | #endif 296 | 297 | if (request != nil) { 298 | // display the request 299 | self.initialRequest = request; 300 | 301 | NSWindow *parentWindow = self.sheetModalForWindow; 302 | if (parentWindow) { 303 | [self setupSheetTerminationHandling]; 304 | 305 | #if GTM_USE_BEGIN_SHEET 306 | NSWindow *sheet = [self window]; 307 | [parentWindow beginSheet:sheet completionHandler:^(NSModalResponse returnCode) { 308 | [sheet orderOut:self]; 309 | 310 | self.sheetModalForWindow = nil; 311 | }]; 312 | #else 313 | NSWindow *sheet = [self window]; 314 | [NSApp beginSheet:sheet 315 | modalForWindow:parentWindow 316 | modalDelegate:self 317 | didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) 318 | contextInfo:nil]; 319 | #endif 320 | } else { 321 | // modeless 322 | [self showWindow:self]; 323 | } 324 | isWindowShown_ = YES; 325 | } else { 326 | // request was nil 327 | [self destroyWindow]; 328 | } 329 | } 330 | 331 | #if !GTM_USE_BEGIN_SHEET 332 | - (void)sheetDidEnd:(NSWindow *)sheet 333 | returnCode:(NSInteger)returnCode 334 | contextInfo:(void *)contextInfo { 335 | [sheet orderOut:self]; 336 | 337 | self.sheetModalForWindow = nil; 338 | } 339 | #endif 340 | 341 | - (void)setupSheetTerminationHandling { 342 | NSWindow *sheet = [self window]; 343 | 344 | SEL sel = @selector(setPreventsApplicationTerminationWhenModal:); 345 | if ([sheet respondsToSelector:sel]) { 346 | // setPreventsApplicationTerminationWhenModal is available in NSWindow 347 | // on 10.6 and later 348 | BOOL boolVal = !self.shouldAllowApplicationTermination; 349 | NSMethodSignature *sig = [sheet methodSignatureForSelector:sel]; 350 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; 351 | [invocation setSelector:sel]; 352 | [invocation setTarget:sheet]; 353 | [invocation setArgument:&boolVal atIndex:2]; 354 | [invocation invoke]; 355 | } 356 | } 357 | 358 | - (void)destroyWindow { 359 | // no request; close the window 360 | 361 | // Avoid more callbacks after the close happens, as the window 362 | // controller may be gone. 363 | // 364 | // We don't want to do this during handling of webView:resource:willSendRequest: as 365 | // that causes a crash (during non-Google sign-in) so defer it to a later runloop. 366 | [self.webView performSelectorOnMainThread:@selector(stopLoading:) 367 | withObject:nil 368 | waitUntilDone:NO]; 369 | 370 | NSWindow *parentWindow = self.sheetModalForWindow; 371 | if (parentWindow) { 372 | NSWindow * _Nonnull ownWindow = (NSWindow * _Nonnull)self.window; 373 | #if GTM_USE_BEGIN_SHEET 374 | [parentWindow endSheet:ownWindow]; 375 | #else 376 | [NSApp endSheet:ownWindow]; 377 | #endif 378 | } else { 379 | // defer closing the window, in case we're responding to some window event 380 | [[self window] performSelector:@selector(close) 381 | withObject:nil 382 | afterDelay:0.1 383 | inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; 384 | 385 | } 386 | isWindowShown_ = NO; 387 | } 388 | 389 | - (void)handlePrematureWindowClose { 390 | if (!hasDoneFinalRedirect_) { 391 | // tell the sign-in object to tell the user's finished method 392 | // that we're done 393 | [self.signIn windowWasClosed]; 394 | hasDoneFinalRedirect_ = YES; 395 | } 396 | } 397 | 398 | - (void)signIn:(GTMOAuth2SignIn *)signIn finishedWithAuth:(GTMOAuth2Authentication *)auth error:(NSError *)error { 399 | if (!hasCalledFinished_) { 400 | hasCalledFinished_ = YES; 401 | 402 | if (error == nil) { 403 | BOOL shouldUseKeychain = [self shouldUseKeychain]; 404 | if (shouldUseKeychain) { 405 | BOOL canAuthorize = auth.canAuthorize; 406 | BOOL isKeychainChecked = ([keychainCheckbox_ state] == NSOnState); 407 | 408 | NSString *keychainItemName = self.keychainItemName; 409 | 410 | if (isKeychainChecked && canAuthorize) { 411 | // save the auth params in the keychain 412 | [[self class] saveAuthToKeychainForName:keychainItemName 413 | authentication:auth]; 414 | } else { 415 | // remove the auth params from the keychain 416 | [[self class] removeAuthFromKeychainForName:keychainItemName]; 417 | } 418 | } 419 | } 420 | 421 | if (delegate_ && finishedSelector_) { 422 | SEL sel = finishedSelector_; 423 | NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel]; 424 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; 425 | [invocation setSelector:sel]; 426 | [invocation setTarget:delegate_]; 427 | [invocation setArgument:&self atIndex:2]; 428 | [invocation setArgument:&auth atIndex:3]; 429 | [invocation setArgument:&error atIndex:4]; 430 | [invocation invoke]; 431 | } 432 | 433 | [delegate_ autorelease]; 434 | delegate_ = nil; 435 | 436 | #if NS_BLOCKS_AVAILABLE 437 | if (completionBlock_) { 438 | completionBlock_(auth, error); 439 | 440 | // release the block here to avoid a retain loop on the controller 441 | [completionBlock_ autorelease]; 442 | completionBlock_ = nil; 443 | } 444 | #endif 445 | } 446 | } 447 | 448 | static Class gSignInClass = Nil; 449 | 450 | + (Class)signInClass { 451 | if (gSignInClass == Nil) { 452 | gSignInClass = [GTMOAuth2SignIn class]; 453 | } 454 | return gSignInClass; 455 | } 456 | 457 | + (void)setSignInClass:(Class)theClass { 458 | gSignInClass = theClass; 459 | } 460 | 461 | #pragma mark Token Revocation 462 | 463 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 464 | + (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth { 465 | [[self signInClass] revokeTokenForGoogleAuthentication:auth]; 466 | } 467 | #endif 468 | 469 | #pragma mark WebView methods 470 | 471 | - (NSURLRequest *)webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource { 472 | // override WebKit's cookie storage with our own to avoid cookie persistence 473 | // across sign-ins and interaction with the Safari browser's sign-in state 474 | [self handleCookiesForResponse:redirectResponse]; 475 | request = [self addCookiesToRequest:request]; 476 | 477 | if (!hasDoneFinalRedirect_) { 478 | hasDoneFinalRedirect_ = [self.signIn requestRedirectedToRequest:request]; 479 | if (hasDoneFinalRedirect_) { 480 | // signIn has told the window to close 481 | return nil; 482 | } 483 | } 484 | return request; 485 | } 486 | 487 | - (void)webView:(WebView *)sender resource:(id)identifier didReceiveResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)dataSource { 488 | // override WebKit's cookie storage with our own 489 | [self handleCookiesForResponse:response]; 490 | } 491 | 492 | - (void)webView:(WebView *)sender resource:(id)identifier didFinishLoadingFromDataSource:(WebDataSource *)dataSource { 493 | NSString *title = [sender stringByEvaluatingJavaScriptFromString:@"document.title"]; 494 | if ([title length] > 0) { 495 | [self.signIn titleChanged:title]; 496 | } 497 | 498 | [signIn_ cookiesChanged:(NSHTTPCookieStorage *)cookieStorage_]; 499 | } 500 | 501 | - (void)webView:(WebView *)sender resource:(id)identifier didFailLoadingWithError:(NSError *)error fromDataSource:(WebDataSource *)dataSource { 502 | [self.signIn loadFailedWithError:error]; 503 | } 504 | 505 | - (void)windowWillClose:(NSNotification *)note { 506 | if (isWindowShown_) { 507 | [self handlePrematureWindowClose]; 508 | } 509 | isWindowShown_ = NO; 510 | } 511 | 512 | - (void)webView:(WebView *)webView 513 | decidePolicyForNewWindowAction:(NSDictionary *)actionInformation 514 | request:(NSURLRequest *)request 515 | newFrameName:(NSString *)frameName 516 | decisionListener:(id)listener { 517 | SEL sel = self.externalRequestSelector; 518 | if (sel) { 519 | [delegate_ performSelector:sel 520 | withObject:self 521 | withObject:request]; 522 | } else { 523 | // default behavior is to open the URL in NSWorkspace's default browser 524 | NSURL *url = [request URL]; 525 | [[NSWorkspace sharedWorkspace] openURL:url]; 526 | } 527 | [listener ignore]; 528 | } 529 | 530 | #pragma mark Cookie management 531 | 532 | // Rather than let the WebView use Safari's default cookie storage, we intercept 533 | // requests and response to segregate and later discard cookies from signing in. 534 | // 535 | // This allows the application to actually sign out by discarding the auth token 536 | // rather than the user being kept signed in by the cookies. 537 | 538 | - (void)handleCookiesForResponse:(NSURLResponse *)response { 539 | if (self.shouldPersistUser) { 540 | // we'll let WebKit handle the cookies; they'll persist across apps 541 | // and across runs of this app 542 | return; 543 | } 544 | 545 | if ([response respondsToSelector:@selector(allHeaderFields)]) { 546 | // grab the cookies from the header as NSHTTPCookies and store them locally 547 | NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields]; 548 | if (headers) { 549 | NSURL *url = [response URL]; 550 | NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headers 551 | forURL:url]; 552 | if ([cookies count] > 0) { 553 | [cookieStorage_ setCookies:cookies]; 554 | } 555 | } 556 | } 557 | } 558 | 559 | - (NSURLRequest *)addCookiesToRequest:(NSURLRequest *)request { 560 | if (self.shouldPersistUser) { 561 | // we'll let WebKit handle the cookies; they'll persist across apps 562 | // and across runs of this app 563 | return request; 564 | } 565 | 566 | // override WebKit's usual automatic storage of cookies 567 | NSMutableURLRequest *mutableRequest = [[request mutableCopy] autorelease]; 568 | [mutableRequest setHTTPShouldHandleCookies:NO]; 569 | 570 | // add our locally-stored cookies for this URL, if any 571 | NSArray *cookies = [cookieStorage_ cookiesForURL:(NSURL * _Nonnull)request.URL]; 572 | if ([cookies count] > 0) { 573 | NSDictionary *headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies]; 574 | NSString *cookieHeader = [headers objectForKey:@"Cookie"]; 575 | if (cookieHeader) { 576 | [mutableRequest setValue:cookieHeader forHTTPHeaderField:@"Cookie"]; 577 | } 578 | } 579 | return mutableRequest; 580 | } 581 | 582 | #pragma mark Keychain support 583 | 584 | + (NSString *)prefsKeyForName:(NSString *)keychainItemName { 585 | NSString *result = [@"OAuth2: " stringByAppendingString:keychainItemName]; 586 | return result; 587 | } 588 | 589 | + (BOOL)saveAuthToKeychainForName:(NSString *)keychainItemName 590 | authentication:(GTMOAuth2Authentication *)auth { 591 | 592 | [self removeAuthFromKeychainForName:keychainItemName]; 593 | 594 | // don't save unless we have a token that can really authorize requests 595 | if (!auth.canAuthorize) return NO; 596 | 597 | // make a response string containing the values we want to save 598 | NSString *password = [auth persistenceResponseString]; 599 | 600 | SecKeychainRef defaultKeychain = NULL; 601 | SecKeychainItemRef *dontWantItemRef= NULL; 602 | const char *utf8ServiceName = [keychainItemName UTF8String]; 603 | const char *utf8Password = [password UTF8String]; 604 | 605 | OSStatus err = SecKeychainAddGenericPassword(defaultKeychain, 606 | (UInt32) strlen(utf8ServiceName), utf8ServiceName, 607 | (UInt32) strlen(kKeychainAccountName), kKeychainAccountName, 608 | (UInt32) strlen(utf8Password), utf8Password, 609 | dontWantItemRef); 610 | BOOL didSucceed = (err == noErr); 611 | if (didSucceed) { 612 | // write to preferences that we have a keychain item (so we know later 613 | // that we can read from the keychain without raising a permissions dialog) 614 | NSString *prefKey = [self prefsKeyForName:keychainItemName]; 615 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 616 | [defaults setBool:YES forKey:prefKey]; 617 | } 618 | 619 | return didSucceed; 620 | } 621 | 622 | + (BOOL)removeAuthFromKeychainForName:(NSString *)keychainItemName { 623 | 624 | SecKeychainRef defaultKeychain = NULL; 625 | SecKeychainItemRef itemRef = NULL; 626 | const char *utf8ServiceName = [keychainItemName UTF8String]; 627 | 628 | // we don't really care about the password here, we just want to 629 | // get the SecKeychainItemRef so we can delete it. 630 | OSStatus err = SecKeychainFindGenericPassword (defaultKeychain, 631 | (UInt32) strlen(utf8ServiceName), utf8ServiceName, 632 | (UInt32) strlen(kKeychainAccountName), kKeychainAccountName, 633 | 0, NULL, // ignore password 634 | &itemRef); 635 | if (err != noErr) { 636 | // failure to find is success 637 | return YES; 638 | } else { 639 | // found something, so delete it 640 | err = SecKeychainItemDelete(itemRef); 641 | CFRelease(itemRef); 642 | 643 | // remove our preference key 644 | NSString *prefKey = [self prefsKeyForName:keychainItemName]; 645 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 646 | [defaults removeObjectForKey:prefKey]; 647 | 648 | return (err == noErr); 649 | } 650 | } 651 | 652 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 653 | + (GTMOAuth2Authentication *)authForGoogleFromKeychainForName:(NSString *)keychainItemName 654 | clientID:(NSString *)clientID 655 | clientSecret:(NSString *)clientSecret { 656 | Class signInClass = [self signInClass]; 657 | NSURL *tokenURL = [signInClass googleTokenURL]; 658 | NSString *redirectURI = [signInClass nativeClientRedirectURI]; 659 | 660 | GTMOAuth2Authentication *auth; 661 | auth = [GTMOAuth2Authentication authenticationWithServiceProvider:kGTMOAuth2ServiceProviderGoogle 662 | tokenURL:tokenURL 663 | redirectURI:redirectURI 664 | clientID:clientID 665 | clientSecret:clientSecret]; 666 | 667 | [GTMOAuth2WindowController authorizeFromKeychainForName:keychainItemName 668 | authentication:auth]; 669 | return auth; 670 | } 671 | #endif 672 | 673 | + (BOOL)authorizeFromKeychainForName:(NSString *)keychainItemName 674 | authentication:(GTMOAuth2Authentication *)newAuth { 675 | [newAuth setAccessToken:nil]; 676 | 677 | // before accessing the keychain, check preferences to verify that we've 678 | // previously saved a token to the keychain (so we don't needlessly raise 679 | // a keychain access permission dialog) 680 | NSString *prefKey = [self prefsKeyForName:keychainItemName]; 681 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 682 | BOOL flag = [defaults boolForKey:prefKey]; 683 | if (!flag) { 684 | return NO; 685 | } 686 | 687 | BOOL didGetTokens = NO; 688 | 689 | SecKeychainRef defaultKeychain = NULL; 690 | const char *utf8ServiceName = [keychainItemName UTF8String]; 691 | SecKeychainItemRef *dontWantItemRef = NULL; 692 | 693 | void *passwordBuff = NULL; 694 | UInt32 passwordBuffLength = 0; 695 | 696 | OSStatus err = SecKeychainFindGenericPassword(defaultKeychain, 697 | (UInt32) strlen(utf8ServiceName), utf8ServiceName, 698 | (UInt32) strlen(kKeychainAccountName), kKeychainAccountName, 699 | &passwordBuffLength, &passwordBuff, 700 | dontWantItemRef); 701 | if (err == noErr && passwordBuff != NULL) { 702 | 703 | NSString *password = [[[NSString alloc] initWithBytes:passwordBuff 704 | length:passwordBuffLength 705 | encoding:NSUTF8StringEncoding] autorelease]; 706 | 707 | // free the password buffer that was allocated above 708 | SecKeychainItemFreeContent(NULL, passwordBuff); 709 | 710 | if (password != nil) { 711 | [newAuth setKeysForResponseString:password]; 712 | didGetTokens = YES; 713 | } 714 | } 715 | return didGetTokens; 716 | } 717 | 718 | #pragma mark User Properties 719 | 720 | - (void)setProperty:(id)obj forKey:(NSString *)key { 721 | if (obj == nil) { 722 | // User passed in nil, so delete the property 723 | [properties_ removeObjectForKey:key]; 724 | } else { 725 | // Be sure the property dictionary exists 726 | if (properties_ == nil) { 727 | [self setProperties:[NSMutableDictionary dictionary]]; 728 | } 729 | [properties_ setObject:obj forKey:key]; 730 | } 731 | } 732 | 733 | - (id)propertyForKey:(NSString *)key { 734 | id obj = [properties_ objectForKey:key]; 735 | 736 | // Be sure the returned pointer has the life of the autorelease pool, 737 | // in case self is released immediately 738 | return [[obj retain] autorelease]; 739 | } 740 | 741 | #pragma mark Accessors 742 | 743 | - (GTMOAuth2Authentication *)authentication { 744 | return self.signIn.authentication; 745 | } 746 | 747 | - (void)setNetworkLossTimeoutInterval:(NSTimeInterval)val { 748 | self.signIn.networkLossTimeoutInterval = val; 749 | } 750 | 751 | - (NSTimeInterval)networkLossTimeoutInterval { 752 | return self.signIn.networkLossTimeoutInterval; 753 | } 754 | 755 | - (BOOL)shouldUseKeychain { 756 | NSString *name = self.keychainItemName; 757 | return ([name length] > 0); 758 | } 759 | 760 | @end 761 | 762 | #endif // #if !TARGET_OS_IPHONE 763 | 764 | #endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 765 | -------------------------------------------------------------------------------- /Source/Touch/GTMOAuth2ViewControllerTouch.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // 17 | // GTMOAuth2ViewControllerTouch.h 18 | // 19 | // This view controller for iPhone handles sign-in via OAuth to Google or 20 | // other services. 21 | // 22 | // This controller is not reusable; create a new instance of this controller 23 | // every time the user will sign in. 24 | // 25 | 26 | #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 27 | 28 | #import 29 | 30 | #if TARGET_OS_IPHONE 31 | 32 | #if defined(__IPHONE_9_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0) 33 | #define GTMOAUTH2AUTHENTICATION_DEPRECATE_OLD_ENUMS 1 34 | #endif 35 | 36 | 37 | #import 38 | 39 | #import "GTMOAuth2Authentication.h" 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | extern NSString *const kGTMOAuth2KeychainErrorDomain; 46 | 47 | // Notifications that the view controller is swapping out and back in cookies. 48 | // Apps may use this to avoid relying on the cookie store while view controller 49 | // has them swapped out. 50 | extern NSString *const kGTMOAuth2CookiesWillSwapOut; 51 | extern NSString *const kGTMOAuth2CookiesDidSwapIn; 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | @class GTMOAuth2SignIn; 58 | @class GTMOAuth2ViewControllerTouch; 59 | 60 | typedef void (^GTMOAuth2ViewControllerCompletionHandler)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error); 61 | 62 | @interface GTMOAuth2ViewControllerTouch : UIViewController { 63 | @private 64 | UIButton *backButton_; 65 | UIButton *forwardButton_; 66 | UIActivityIndicatorView *initialActivityIndicator_; 67 | UIView *navButtonsView_; 68 | UIBarButtonItem *rightBarButtonItem_; 69 | UIWebView *webView_; 70 | 71 | // The object responsible for the sign-in networking sequence; it holds 72 | // onto the authentication object as well. 73 | GTMOAuth2SignIn *signIn_; 74 | 75 | // the page request to load when awakeFromNib occurs 76 | NSURLRequest *request_; 77 | 78 | // The user we're calling back 79 | // 80 | // The delegate is retained only until the callback is invoked 81 | // or the sign-in is canceled 82 | id delegate_; 83 | SEL finishedSelector_; 84 | 85 | #if NS_BLOCKS_AVAILABLE 86 | GTMOAuth2ViewControllerCompletionHandler completionBlock_; 87 | 88 | void (^popViewBlock_)(void); 89 | #endif 90 | 91 | NSString *keychainItemName_; 92 | CFTypeRef keychainItemAccessibility_; 93 | 94 | // if non-nil, the html string to be displayed immediately upon opening 95 | // of the web view 96 | NSString *initialHTMLString_; 97 | 98 | // set to 1 or -1 if the user sets the showsInitialActivityIndicator 99 | // property 100 | int mustShowActivityIndicator_; 101 | 102 | // if non-nil, the URL for which cookies will be deleted when the 103 | // browser view is dismissed 104 | NSURL *browserCookiesURL_; 105 | 106 | id userData_; 107 | NSMutableDictionary *properties_; 108 | 109 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 110 | // We delegate the decision to our owning NavigationController (if any). 111 | // But, the NavigationController will call us back, and ask us. 112 | // BOOL keeps us from infinite looping. 113 | BOOL isInsideShouldAutorotateToInterfaceOrientation_; 114 | #endif 115 | 116 | // YES, when view first shown in this signIn session. 117 | BOOL isViewShown_; 118 | 119 | // YES, after the view has fully transitioned in. 120 | BOOL didViewAppear_; 121 | 122 | // YES between sends of start and stop notifications 123 | BOOL hasNotifiedWebViewStartedLoading_; 124 | 125 | // To prevent us from calling our delegate's selector more than once. 126 | BOOL hasCalledFinished_; 127 | 128 | // Set in a webView callback. 129 | BOOL hasDoneFinalRedirect_; 130 | 131 | // Set during the pop initiated by the sign-in object; otherwise, 132 | // viewWillDisappear indicates that some external change of the view 133 | // has stopped the sign-in. 134 | BOOL didDismissSelf_; 135 | 136 | // Work around default cookie policy bug in iOS 7; see comments in viewWillAppear. 137 | NSHTTPCookieAcceptPolicy savedCookiePolicy_; 138 | } 139 | 140 | // the application and service name to use for saving the auth tokens 141 | // to the keychain 142 | @property (nonatomic, copy) NSString *keychainItemName; 143 | 144 | // the keychain item accessibility is a system constant for use 145 | // with kSecAttrAccessible. 146 | // 147 | // Since it's a system constant, we do not need to retain it. 148 | @property (nonatomic, assign) CFTypeRef keychainItemAccessibility; 149 | 150 | // optional html string displayed immediately upon opening the web view 151 | // 152 | // This string is visible just until the sign-in web page loads, and 153 | // may be used for a "Loading..." type of message or to set the 154 | // initial view color 155 | @property (nonatomic, copy) NSString *initialHTMLString; 156 | 157 | // an activity indicator shows during initial webview load when no initial HTML 158 | // string is specified, but the activity indicator can be forced to be shown 159 | // with this property 160 | @property (nonatomic, assign) BOOL showsInitialActivityIndicator; 161 | 162 | // the underlying object to hold authentication tokens and authorize http 163 | // requests 164 | @property (nonatomic, retain, readonly) GTMOAuth2Authentication *authentication; 165 | 166 | // the underlying object which performs the sign-in networking sequence 167 | @property (nonatomic, retain, readonly) GTMOAuth2SignIn *signIn; 168 | 169 | // user interface elements 170 | @property (nonatomic, retain) IBOutlet UIButton *backButton; 171 | @property (nonatomic, retain) IBOutlet UIButton *forwardButton; 172 | @property (nonatomic, retain) IBOutlet UIActivityIndicatorView *initialActivityIndicator; 173 | @property (nonatomic, retain) IBOutlet UIView *navButtonsView; 174 | @property (nonatomic, retain) IBOutlet UIBarButtonItem *rightBarButtonItem; 175 | @property (nonatomic, retain) IBOutlet UIWebView *webView; 176 | 177 | #if NS_BLOCKS_AVAILABLE 178 | // An optional block to be called when the view should be popped. If not set, 179 | // the view controller will use its navigation controller to pop the view. 180 | @property (nonatomic, copy) void (^popViewBlock)(void); 181 | #endif 182 | 183 | // the default timeout for an unreachable network during display of the 184 | // sign-in page is 30 seconds; set this to 0 to have no timeout 185 | @property (nonatomic, assign) NSTimeInterval networkLossTimeoutInterval; 186 | 187 | // if set, cookies are deleted for this URL when the view is hidden 188 | // 189 | // This is now vestigial and ignored; all cookies are temporarily removed 190 | // from cookie storage when sign-in begins. 191 | @property (nonatomic, retain) NSURL *browserCookiesURL; 192 | 193 | // userData is retained for the convenience of the caller 194 | @property (nonatomic, retain) id userData; 195 | 196 | // Stored property values are retained for the convenience of the caller 197 | - (void)setProperty:(id)obj forKey:(NSString *)key; 198 | - (id)propertyForKey:(NSString *)key; 199 | 200 | @property (nonatomic, retain) NSDictionary *properties; 201 | 202 | // Method for creating a controller to authenticate to Google services 203 | // 204 | // scope is the requested scope of authorization 205 | // (like "http://www.google.com/m8/feeds") 206 | // 207 | // keychain item name is used for storing the token on the keychain, 208 | // keychainItemName should be like "My Application: Google Latitude" 209 | // (or set to nil if no persistent keychain storage is desired) 210 | // 211 | // the delegate is retained only until the finished selector is invoked 212 | // or the sign-in is canceled 213 | // 214 | // If you don't like the default nibName and bundle, you can change them 215 | // using the UIViewController properties once you've made one of these. 216 | // 217 | // finishedSelector is called after authentication completes. It should follow 218 | // this signature. 219 | // 220 | // - (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController 221 | // finishedWithAuth:(GTMOAuth2Authentication *)auth 222 | // error:(NSError *)error; 223 | // 224 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 225 | + (id)controllerWithScope:(NSString *)scope 226 | clientID:(NSString *)clientID 227 | clientSecret:(NSString *)clientSecret 228 | keychainItemName:(NSString *)keychainItemName 229 | delegate:(id)delegate 230 | finishedSelector:(SEL)finishedSelector; 231 | 232 | - (id)initWithScope:(NSString *)scope 233 | clientID:(NSString *)clientID 234 | clientSecret:(NSString *)clientSecret 235 | keychainItemName:(NSString *)keychainItemName 236 | delegate:(id)delegate 237 | finishedSelector:(SEL)finishedSelector; 238 | 239 | #if NS_BLOCKS_AVAILABLE 240 | + (id)controllerWithScope:(NSString *)scope 241 | clientID:(NSString *)clientID 242 | clientSecret:(NSString *)clientSecret 243 | keychainItemName:(NSString *)keychainItemName 244 | completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler; 245 | 246 | - (id)initWithScope:(NSString *)scope 247 | clientID:(NSString *)clientID 248 | clientSecret:(NSString *)clientSecret 249 | keychainItemName:(NSString *)keychainItemName 250 | completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler; 251 | #endif 252 | #endif 253 | 254 | // Create a controller for authenticating to non-Google services, taking 255 | // explicit endpoint URLs and an authentication object 256 | + (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth 257 | authorizationURL:(NSURL *)authorizationURL 258 | keychainItemName:(NSString *)keychainItemName // may be nil 259 | delegate:(id)delegate 260 | finishedSelector:(SEL)finishedSelector; 261 | 262 | // This is the designated initializer 263 | - (id)initWithAuthentication:(GTMOAuth2Authentication *)auth 264 | authorizationURL:(NSURL *)authorizationURL 265 | keychainItemName:(NSString *)keychainItemName 266 | delegate:(id)delegate 267 | finishedSelector:(SEL)finishedSelector; 268 | 269 | #if NS_BLOCKS_AVAILABLE 270 | + (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth 271 | authorizationURL:(NSURL *)authorizationURL 272 | keychainItemName:(NSString *)keychainItemName // may be nil 273 | completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler; 274 | 275 | - (id)initWithAuthentication:(GTMOAuth2Authentication *)auth 276 | authorizationURL:(NSURL *)authorizationURL 277 | keychainItemName:(NSString *)keychainItemName 278 | completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler; 279 | #endif 280 | 281 | // subclasses may override authNibName to specify a custom name 282 | + (NSString *)authNibName; 283 | 284 | // subclasses may override authNibBundle to specify a custom bundle 285 | + (NSBundle *)authNibBundle; 286 | 287 | // subclasses may override setUpNavigation to provide their own navigation 288 | // controls 289 | - (void)setUpNavigation; 290 | 291 | // Swaps out the system cookies. The default implementation saves the system 292 | // cookies and then switches to the cookies used for sign-in, initally empty. 293 | // 294 | // subclasses may override swapOutCookies to implement their own cookie 295 | // management scheme. 296 | - (void)swapOutCookies; 297 | 298 | // Swaps in the system cookies that were swapped out. The default implementation 299 | // saves the cookies used for sign-in and then restores the system cookies 300 | // that were saved in |swapOutCookies|. 301 | // 302 | // subclasses may override swapInCookies to implement their own cookie 303 | // management scheme. 304 | - (void)swapInCookies; 305 | 306 | // Returns the cookie storage where the system cookies are stored. The default 307 | // implementation returns [NSHTTPCookieStorage sharedHTTPCookieStorage]. 308 | // 309 | // Subclasses may override systemCookieStorage to implement their own cookie 310 | // management. 311 | - (NSHTTPCookieStorage *)systemCookieStorage; 312 | 313 | // apps may replace the sign-in class with their own subclass of it 314 | + (Class)signInClass; 315 | + (void)setSignInClass:(Class)theClass; 316 | 317 | - (void)cancelSigningIn; 318 | 319 | // revocation of an authorized token from Google 320 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 321 | + (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth; 322 | #endif 323 | 324 | // 325 | // Keychain 326 | // 327 | 328 | // create an authentication object for Google services from the access 329 | // token and secret stored in the keychain; if no token is available, return 330 | // an unauthorized auth object. OK to pass NULL for the error parameter. 331 | #if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT 332 | + (GTMOAuth2Authentication *)authForGoogleFromKeychainForName:(NSString *)keychainItemName 333 | clientID:(NSString *)clientID 334 | clientSecret:(NSString *)clientSecret 335 | error:(NSError **)error; 336 | // Equivalent to calling the method above with a NULL error parameter. 337 | + (GTMOAuth2Authentication *)authForGoogleFromKeychainForName:(NSString *)keychainItemName 338 | clientID:(NSString *)clientID 339 | clientSecret:(NSString *)clientSecret; 340 | #endif 341 | 342 | // add tokens from the keychain, if available, to the authentication object 343 | // 344 | // returns YES if the authentication object was authorized from the keychain 345 | + (BOOL)authorizeFromKeychainForName:(NSString *)keychainItemName 346 | authentication:(GTMOAuth2Authentication *)auth 347 | error:(NSError **)error; 348 | 349 | // method for deleting the stored access token and secret, useful for "signing 350 | // out" 351 | + (BOOL)removeAuthFromKeychainForName:(NSString *)keychainItemName; 352 | 353 | // method for saving the stored access token and secret 354 | // 355 | // returns YES if the save was successful. OK to pass NULL for the error 356 | // parameter. 357 | + (BOOL)saveParamsToKeychainForName:(NSString *)keychainItemName 358 | accessibility:(CFTypeRef)accessibility 359 | authentication:(GTMOAuth2Authentication *)auth 360 | error:(NSError **)error; 361 | 362 | // older version, defaults to kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly 363 | + (BOOL)saveParamsToKeychainForName:(NSString *)keychainItemName 364 | authentication:(GTMOAuth2Authentication *)auth; 365 | 366 | @end 367 | 368 | // To function, GTMOAuth2ViewControllerTouch needs a certain amount of access 369 | // to the iPhone's keychain. To keep things simple, its keychain access is 370 | // broken out into a helper class. We declare it here in case you'd like to use 371 | // it too, to store passwords. 372 | 373 | typedef NS_ENUM(NSInteger, GTMOAuth2KeychainError) { 374 | GTMOAuth2KeychainErrorBadArguments = -1301, 375 | GTMOAuth2KeychainErrorNoPassword = -1302 376 | }; 377 | 378 | #if !GTMOAUTH2AUTHENTICATION_DEPRECATE_OLD_ENUMS 379 | #define kGTMOAuth2KeychainErrorBadArguments GTMOAuth2KeychainErrorBadArguments 380 | #define kGTMOAuth2KeychainErrorNoPassword GTMOAuth2KeychainErrorNoPassword 381 | #endif 382 | 383 | 384 | @interface GTMOAuth2Keychain : NSObject 385 | 386 | + (GTMOAuth2Keychain *)defaultKeychain; 387 | 388 | // OK to pass nil for the error parameter. 389 | - (NSString *)passwordForService:(NSString *)service 390 | account:(NSString *)account 391 | error:(NSError **)error; 392 | 393 | // OK to pass nil for the error parameter. 394 | - (BOOL)removePasswordForService:(NSString *)service 395 | account:(NSString *)account 396 | error:(NSError **)error; 397 | 398 | // OK to pass nil for the error parameter. 399 | // 400 | // accessibility should be one of the constants for kSecAttrAccessible 401 | // such as kSecAttrAccessibleWhenUnlocked 402 | - (BOOL)setPassword:(NSString *)password 403 | forService:(NSString *)service 404 | accessibility:(CFTypeRef)accessibility 405 | account:(NSString *)account 406 | error:(NSError **)error; 407 | 408 | // For unit tests: allow setting a mock object 409 | + (void)setDefaultKeychain:(GTMOAuth2Keychain *)keychain; 410 | 411 | @end 412 | 413 | #endif // TARGET_OS_IPHONE 414 | 415 | #endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES 416 | -------------------------------------------------------------------------------- /Source/Touch/GTMOAuth2ViewTouch.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1792 5 | 12F2560 6 | 5056 7 | 1187.40 8 | 626.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 3733 12 | 13 | 14 | IBProxyObject 15 | IBUIActivityIndicatorView 16 | IBUIBarButtonItem 17 | IBUIButton 18 | IBUINavigationItem 19 | IBUIView 20 | IBUIWebView 21 | 22 | 23 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 24 | 25 | 26 | PluginDependencyRecalculationVersion 27 | 28 | 29 | 30 | 31 | IBFilesOwner 32 | IBCocoaTouchFramework 33 | 34 | 35 | IBFirstResponder 36 | IBCocoaTouchFramework 37 | 38 | 39 | OAuth 40 | IBCocoaTouchFramework 41 | 42 | 43 | IBCocoaTouchFramework 44 | 1 45 | 46 | 47 | 48 | 292 49 | 50 | 51 | 52 | 292 53 | {30, 30} 54 | 55 | 56 | 57 | NO 58 | NO 59 | IBCocoaTouchFramework 60 | 0 61 | 0 62 | 63 | 3 64 | MQA 65 | 66 | 67 | {0, -2} 68 | 69 | 70 | 2 71 | MC41OTYwNzg0NiAwLjY4NjI3NDUzIDAuOTUyOTQxMjQgMC42MDAwMDAwMgA 72 | 73 | 74 | 3 75 | MC41AA 76 | 77 | 78 | Helvetica-Bold 79 | Helvetica 80 | 2 81 | 24 82 | 83 | 84 | Helvetica-Bold 85 | 24 86 | 16 87 | 88 | 89 | 90 | 91 | 292 92 | {{30, 0}, {30, 30}} 93 | 94 | 95 | 96 | NO 97 | NO 98 | IBCocoaTouchFramework 99 | 0 100 | 0 101 | 102 | 103 | {0, -2} 104 | 105 | 106 | 2 107 | MC41ODQzMTM3NSAwLjY3NDUwOTgyIDAuOTUyOTQxMjQgMC42MDAwMDAwMgA 108 | 109 | 110 | 111 | 112 | 113 | 114 | {60, 30} 115 | 116 | 117 | 118 | 119 | 3 120 | MSAwAA 121 | 122 | NO 123 | NO 124 | 125 | 3 126 | 3 127 | 128 | IBCocoaTouchFramework 129 | 130 | 131 | 132 | 274 133 | 134 | 135 | 136 | 274 137 | {320, 460} 138 | 139 | 140 | 141 | 1 142 | MSAxIDEAA 143 | 144 | YES 145 | YES 146 | IBCocoaTouchFramework 147 | 1 148 | YES 149 | 150 | 151 | 152 | 301 153 | {{150, 115}, {20, 20}} 154 | 155 | _NS:9 156 | NO 157 | IBCocoaTouchFramework 158 | NO 159 | YES 160 | 2 161 | 162 | 163 | {320, 460} 164 | 165 | 166 | 3 167 | MQA 168 | 169 | 2 170 | 171 | 172 | IBCocoaTouchFramework 173 | 174 | 175 | 176 | NO 177 | 178 | 179 | 180 | rightBarButtonItem 181 | 182 | 183 | 184 | 20 185 | 186 | 187 | 188 | navButtonsView 189 | 190 | 191 | 192 | 22 193 | 194 | 195 | 196 | backButton 197 | 198 | 199 | 200 | 25 201 | 202 | 203 | 204 | forwardButton 205 | 206 | 207 | 208 | 26 209 | 210 | 211 | 212 | view 213 | 214 | 215 | 216 | 28 217 | 218 | 219 | 220 | webView 221 | 222 | 223 | 224 | 29 225 | 226 | 227 | 228 | initialActivityIndicator 229 | 230 | 231 | 232 | 33 233 | 234 | 235 | 236 | delegate 237 | 238 | 239 | 240 | 9 241 | 242 | 243 | 244 | rightBarButtonItem 245 | 246 | 247 | 248 | 14 249 | 250 | 251 | 252 | goBack 253 | 254 | 255 | 7 256 | 257 | 18 258 | 259 | 260 | 261 | goForward 262 | 263 | 264 | 7 265 | 266 | 19 267 | 268 | 269 | 270 | 271 | 272 | 0 273 | 274 | 275 | 276 | 277 | 278 | -1 279 | 280 | 281 | File's Owner 282 | 283 | 284 | -2 285 | 286 | 287 | 288 | 289 | 6 290 | 291 | 292 | 293 | 294 | 295 | 10 296 | 297 | 298 | 299 | 300 | 15 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 16 310 | 311 | 312 | 313 | 314 | 17 315 | 316 | 317 | 318 | 319 | 27 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 4 329 | 330 | 331 | 332 | 333 | 31 334 | 335 | 336 | 337 | 338 | 339 | 340 | GTMOAuth2ViewControllerTouch 341 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 342 | UIResponder 343 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 344 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 345 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 346 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 347 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 348 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 349 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 350 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 351 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 0 360 | IBCocoaTouchFramework 361 | YES 362 | 363 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 364 | 365 | 366 | YES 367 | 3 368 | 3733 369 | 370 | 371 | --------------------------------------------------------------------------------