├── .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 |
13 |
14 | IBProxyObject
15 | IBUIActivityIndicatorView
16 | IBUIBarButtonItem
17 | IBUIButton
18 | IBUINavigationItem
19 | IBUIView
20 | IBUIWebView
21 |
22 |
23 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
24 |
25 |
29 |
30 |
34 |
38 |
42 |
46 |
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 |
--------------------------------------------------------------------------------