Merge pull request #243 from jcanizales/add-objc-auth-demo

Add objc auth demo
This commit is contained in:
Michael Lumish 2015-07-13 11:44:39 -07:00
commit 1cbf744b58
22 changed files with 1439 additions and 0 deletions

View File

@ -114,6 +114,22 @@ var channel = new Channel("localhost:50051", credentials);
var client = new Greeter.GreeterClient(channel); var client = new Greeter.GreeterClient(channel);
``` ```
###SSL/TLS for server authentication and encryption (Objective-C)
The default for Objective-C is to use SSL/TLS, as that's the most common use case when accessing
remote APIs.
```objective-c
// Base case - With server authentication SSL/TLS
HLWGreeter *client = [[HLWGreeter alloc] initWithHost:@"localhost:50051"];
// Same as using @"https://localhost:50051".
...
// No encryption
HLWGreeter *client = [[HLWGreeter alloc] initWithHost:@"http://localhost:50051"];
// Specifying the HTTP scheme explicitly forces no encryption.
```
###Authenticating with Google (Ruby) ###Authenticating with Google (Ruby)
```ruby ```ruby
# Base case - No encryption/authorization # Base case - No encryption/authorization
@ -195,3 +211,43 @@ $client = new helloworld\GreeterClient(
new Grpc\BaseStub('localhost:50051', $opts)); new Grpc\BaseStub('localhost:50051', $opts));
``` ```
###Authenticating with Google (Objective-C)
This example uses the [Google iOS Sign-In library](https://developers.google.com/identity/sign-in/ios/),
but it's easily extrapolated to any other OAuth2 library.
```objective-c
// Base case - No authentication
[client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) {
...
}];
...
// Authenticating with Google
// When signing the user in, ask her for the relevant scopes.
GIDSignIn.sharedInstance.scopes = @[@"https://www.googleapis.com/auth/grpc-testing"];
...
#import <gRPC/ProtoRPC.h>
// Create a not-yet-started RPC. We want to set the request headers on this object before starting
// it.
ProtoRPC *call =
[client RPCToSayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) {
...
}];
// Set the access token to be used.
NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken;
call.requestMetadata = [NSMutableDictionary dictionaryWithDictionary:
@{@"Authorization": [@"Bearer " stringByAppendingString:accessToken]}];
// Start the RPC.
[call start];
```
You can see a working example app, with a more detailed explanation, [here](https://github.com/grpc/grpc-common/tree/master/objective-c/auth_sample).

View File

@ -0,0 +1,366 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
63E1E9821B28CB2100EF0978 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E9811B28CB2100EF0978 /* main.m */; };
63E1E9851B28CB2100EF0978 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E9841B28CB2100EF0978 /* AppDelegate.m */; };
63E1E9881B28CB2100EF0978 /* SelectUserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E9871B28CB2100EF0978 /* SelectUserViewController.m */; };
63E1E98B1B28CB2100EF0978 /* MakeRPCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E98A1B28CB2100EF0978 /* MakeRPCViewController.m */; };
63E1E98E1B28CB2100EF0978 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 63E1E98C1B28CB2100EF0978 /* Main.storyboard */; };
63E1E9901B28CB2100EF0978 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 63E1E98F1B28CB2100EF0978 /* Images.xcassets */; };
63F5DE481B28F5C100CDD07E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 63F5DE471B28F5C100CDD07E /* GoogleService-Info.plist */; };
832213142AB24DB816D02635 /* libPods-AuthSample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F217A6ECA7F5BD1D5FB5071B /* libPods-AuthSample.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
63E1E97C1B28CB2100EF0978 /* AuthSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AuthSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
63E1E9801B28CB2100EF0978 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
63E1E9811B28CB2100EF0978 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
63E1E9831B28CB2100EF0978 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
63E1E9841B28CB2100EF0978 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
63E1E9861B28CB2100EF0978 /* SelectUserViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SelectUserViewController.h; sourceTree = "<group>"; };
63E1E9871B28CB2100EF0978 /* SelectUserViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SelectUserViewController.m; sourceTree = "<group>"; };
63E1E9891B28CB2100EF0978 /* MakeRPCViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MakeRPCViewController.h; sourceTree = "<group>"; };
63E1E98A1B28CB2100EF0978 /* MakeRPCViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MakeRPCViewController.m; sourceTree = "<group>"; };
63E1E98D1B28CB2100EF0978 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
63E1E98F1B28CB2100EF0978 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
63F5DE471B28F5C100CDD07E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
A387D6CECBCF2EAF2983033A /* Pods-AuthSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AuthSample/Pods-AuthSample.debug.xcconfig"; sourceTree = "<group>"; };
B444176735DA81FBE4B8B80C /* Pods-AuthSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthSample.release.xcconfig"; path = "Pods/Target Support Files/Pods-AuthSample/Pods-AuthSample.release.xcconfig"; sourceTree = "<group>"; };
F217A6ECA7F5BD1D5FB5071B /* libPods-AuthSample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AuthSample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
63E1E9791B28CB2000EF0978 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
832213142AB24DB816D02635 /* libPods-AuthSample.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
021FA0E0B3F5D3D477DDAC10 /* Pods */ = {
isa = PBXGroup;
children = (
A387D6CECBCF2EAF2983033A /* Pods-AuthSample.debug.xcconfig */,
B444176735DA81FBE4B8B80C /* Pods-AuthSample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
4F443572636F3D60F26E870D /* Frameworks */ = {
isa = PBXGroup;
children = (
F217A6ECA7F5BD1D5FB5071B /* libPods-AuthSample.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
63E1E9731B28CB2000EF0978 = {
isa = PBXGroup;
children = (
63E1E97E1B28CB2100EF0978 /* AuthSample */,
63E1E97D1B28CB2100EF0978 /* Products */,
021FA0E0B3F5D3D477DDAC10 /* Pods */,
4F443572636F3D60F26E870D /* Frameworks */,
);
sourceTree = "<group>";
};
63E1E97D1B28CB2100EF0978 /* Products */ = {
isa = PBXGroup;
children = (
63E1E97C1B28CB2100EF0978 /* AuthSample.app */,
);
name = Products;
sourceTree = "<group>";
};
63E1E97E1B28CB2100EF0978 /* AuthSample */ = {
isa = PBXGroup;
children = (
63E1E9861B28CB2100EF0978 /* SelectUserViewController.h */,
63E1E9871B28CB2100EF0978 /* SelectUserViewController.m */,
63E1E9891B28CB2100EF0978 /* MakeRPCViewController.h */,
63E1E98A1B28CB2100EF0978 /* MakeRPCViewController.m */,
63E1E97F1B28CB2100EF0978 /* Supporting Files */,
);
name = AuthSample;
sourceTree = SOURCE_ROOT;
};
63E1E97F1B28CB2100EF0978 /* Supporting Files */ = {
isa = PBXGroup;
children = (
63E1E98C1B28CB2100EF0978 /* Main.storyboard */,
63F5DE471B28F5C100CDD07E /* GoogleService-Info.plist */,
63E1E98F1B28CB2100EF0978 /* Images.xcassets */,
63E1E9801B28CB2100EF0978 /* Info.plist */,
63E1E9831B28CB2100EF0978 /* AppDelegate.h */,
63E1E9841B28CB2100EF0978 /* AppDelegate.m */,
63E1E9811B28CB2100EF0978 /* main.m */,
);
name = "Supporting Files";
path = Misc;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
63E1E97B1B28CB2000EF0978 /* AuthSample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 63E1E9A21B28CB2100EF0978 /* Build configuration list for PBXNativeTarget "AuthSample" */;
buildPhases = (
DAABBA7B5788A39108D7CA83 /* Check Pods Manifest.lock */,
63E1E9781B28CB2000EF0978 /* Sources */,
63E1E9791B28CB2000EF0978 /* Frameworks */,
63E1E97A1B28CB2000EF0978 /* Resources */,
AEFCCC69DD59CE8F6EB769D7 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = AuthSample;
productName = AuthSample;
productReference = 63E1E97C1B28CB2100EF0978 /* AuthSample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
63E1E9741B28CB2000EF0978 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0630;
ORGANIZATIONNAME = gRPC;
TargetAttributes = {
63E1E97B1B28CB2000EF0978 = {
CreatedOnToolsVersion = 6.3.1;
};
};
};
buildConfigurationList = 63E1E9771B28CB2000EF0978 /* Build configuration list for PBXProject "AuthSample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 63E1E9731B28CB2000EF0978;
productRefGroup = 63E1E97D1B28CB2100EF0978 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
63E1E97B1B28CB2000EF0978 /* AuthSample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
63E1E97A1B28CB2000EF0978 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
63E1E98E1B28CB2100EF0978 /* Main.storyboard in Resources */,
63E1E9901B28CB2100EF0978 /* Images.xcassets in Resources */,
63F5DE481B28F5C100CDD07E /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
AEFCCC69DD59CE8F6EB769D7 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AuthSample/Pods-AuthSample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
DAABBA7B5788A39108D7CA83 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
63E1E9781B28CB2000EF0978 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
63E1E98B1B28CB2100EF0978 /* MakeRPCViewController.m in Sources */,
63E1E9851B28CB2100EF0978 /* AppDelegate.m in Sources */,
63E1E9881B28CB2100EF0978 /* SelectUserViewController.m in Sources */,
63E1E9821B28CB2100EF0978 /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
63E1E98C1B28CB2100EF0978 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
63E1E98D1B28CB2100EF0978 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
63E1E9A01B28CB2100EF0978 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
63E1E9A11B28CB2100EF0978 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
63E1E9A31B28CB2100EF0978 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A387D6CECBCF2EAF2983033A /* Pods-AuthSample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Misc/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
63E1E9A41B28CB2100EF0978 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B444176735DA81FBE4B8B80C /* Pods-AuthSample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Misc/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
63E1E9771B28CB2000EF0978 /* Build configuration list for PBXProject "AuthSample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
63E1E9A01B28CB2100EF0978 /* Debug */,
63E1E9A11B28CB2100EF0978 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
63E1E9A21B28CB2100EF0978 /* Build configuration list for PBXNativeTarget "AuthSample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
63E1E9A31B28CB2100EF0978 /* Debug */,
63E1E9A41B28CB2100EF0978 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 63E1E9741B28CB2000EF0978 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:AuthSample.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,35 @@
Pod::Spec.new do |s|
s.name = "AuthTestService"
s.version = "0.0.1"
s.license = "New BSD"
s.ios.deployment_target = "6.0"
s.osx.deployment_target = "10.8"
# Base directory where the .proto files are.
src = "../../protos"
# Directory where the generated files will be place.
dir = "Pods/" + s.name
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
s.prepare_command = <<-CMD
mkdir -p #{dir}
protoc -I #{src} --objc_out=#{dir} --objcgrpc_out=#{dir} #{src}/auth_sample.proto
CMD
s.subspec "Messages" do |ms|
ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}"
ms.header_mappings_dir = dir
ms.requires_arc = false
ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
end
s.subspec "Services" do |ss|
ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}"
ss.header_mappings_dir = dir
ss.requires_arc = true
ss.dependency "gRPC", "~> 0.5"
ss.dependency "#{s.name}/Messages"
end
end

View File

@ -0,0 +1,40 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <UIKit/UIKit.h>
extern NSString * const kTestScope;
@interface MakeRPCViewController : UIViewController
@property(weak, nonatomic) IBOutlet UILabel *mainLabel;
@end

View File

@ -0,0 +1,89 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "MakeRPCViewController.h"
#import <AuthTestService/AuthSample.pbrpc.h>
#import <Google/SignIn.h>
#import <gRPC/ProtoRPC.h>
#include <gRPC/status.h>
NSString * const kTestScope = @"https://www.googleapis.com/auth/xapi.zoo";
static NSString * const kTestHostAddress = @"grpc-test.sandbox.google.com";
@implementation MakeRPCViewController
- (void)viewWillAppear:(BOOL)animated {
// Create a service client and a proto request as usual.
AUTHTestService *client = [[AUTHTestService alloc] initWithHost:kTestHostAddress];
AUTHRequest *request = [AUTHRequest message];
request.fillUsername = YES;
request.fillOauthScope = YES;
// Create a not-yet-started RPC. We want to set the request headers on this object before starting
// it.
__block ProtoRPC *call =
[client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
if (response) {
// This test server responds with the email and scope of the access token it receives.
self.mainLabel.text = [NSString stringWithFormat:@"Used scope: %@ on behalf of user %@",
response.oauthScope, response.username];
} else if (error.code == GRPC_STATUS_UNAUTHENTICATED) {
// Authentication error. OAuth2 specifies we'll receive a challenge header.
NSString *challengeHeader = call.responseMetadata[@"www-authenticate"][0] ?: @"";
self.mainLabel.text =
[@"Invalid credentials. Server challenge:\n" stringByAppendingString:challengeHeader];
} else {
// Any other error.
self.mainLabel.text = [NSString stringWithFormat:@"Unexpected RPC error %li: %@",
(long)error.code, error.localizedDescription];
}
}];
// Set the access token to be used.
NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken;
call.requestMetadata = [NSMutableDictionary dictionaryWithDictionary:
@{@"Authorization": [@"Bearer " stringByAppendingString:accessToken]}];
// Start the RPC.
[call start];
self.mainLabel.text = @"Waiting for RPC to complete...";
}
@end

View File

@ -0,0 +1,38 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@ -0,0 +1,61 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "AppDelegate.h"
#import <Google/SignIn.h>
@implementation AppDelegate
// As instructed in https://developers.google.com/identity/sign-in/ios/sign-in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSError* configureError;
[GGLContext.sharedInstance configureWithError: &configureError];
NSAssert(!configureError, @"Error configuring Google services: %@", configureError);
return YES;
}
// As instructed in https://developers.google.com/identity/sign-in/ios/sign-in
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// This will properly handle the URL that the application receives at the end of the
// authentication process.
return [GIDSignIn.sharedInstance handleURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
@end

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7702" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="49e-Tb-3d3">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<scenes>
<!--Select User-->
<scene sceneID="hNz-n2-bh7">
<objects>
<viewController id="9pv-A4-QxB" userLabel="Select User" customClass="SelectUserViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ia1-K6-d13"/>
<viewControllerLayoutGuide type="bottom" id="4ug-Mw-9AY"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="tsR-hK-woN">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Please sign in." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="KQZ-1w-vlD" userLabel="Main Label">
<rect key="frame" x="222" y="190" width="157" height="28"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Helvetica" family="Helvetica" pointSize="24"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A5M-7J-77L" userLabel="Sub Label">
<rect key="frame" x="301" y="226" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W20-aa-6Xr">
<rect key="frame" x="150" y="370" width="300" height="50"/>
<subviews>
<view contentMode="center" translatesAutoresizingMaskIntoConstraints="NO" id="25e-pE-eb8" customClass="GIDSignInButton">
<rect key="frame" x="42" y="0.0" width="216" height="77"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="216" placeholder="YES" id="Gfn-Hn-HXd"/>
<constraint firstAttribute="height" constant="77" placeholder="YES" id="cy1-xY-er0"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RKb-EE-mCZ">
<rect key="frame" x="0.0" y="0.0" width="300" height="42"/>
<color key="backgroundColor" red="0.0" green="0.50196081399917603" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="300" id="ILy-QC-YPq"/>
<constraint firstAttribute="height" constant="42" id="MFs-aL-fQF"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<state key="normal" title="Sign out">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="didTapSignOut" destination="9pv-A4-QxB" eventType="touchUpInside" id="lcC-7k-Rki"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="25e-pE-eb8" secondAttribute="centerX" id="47f-A8-C2R"/>
<constraint firstAttribute="height" constant="50" id="EDJ-oo-Ix4"/>
<constraint firstItem="25e-pE-eb8" firstAttribute="top" secondItem="W20-aa-6Xr" secondAttribute="top" id="KSP-qb-mCJ"/>
<constraint firstItem="RKb-EE-mCZ" firstAttribute="top" secondItem="W20-aa-6Xr" secondAttribute="top" id="QDr-HV-DXb"/>
<constraint firstAttribute="width" constant="300" id="zWU-pZ-oQ7"/>
<constraint firstAttribute="centerX" secondItem="RKb-EE-mCZ" secondAttribute="centerX" id="zwy-cD-xao"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="KQZ-1w-vlD" secondAttribute="centerX" id="6BV-lF-sBN"/>
<constraint firstItem="KQZ-1w-vlD" firstAttribute="top" secondItem="Ia1-K6-d13" secondAttribute="bottom" constant="170" id="82x-jY-eWa"/>
<constraint firstAttribute="centerX" secondItem="W20-aa-6Xr" secondAttribute="centerX" id="aHL-Ts-LoW"/>
<constraint firstItem="A5M-7J-77L" firstAttribute="top" secondItem="KQZ-1w-vlD" secondAttribute="bottom" constant="8" symbolic="YES" id="cfb-er-3JN"/>
<constraint firstItem="A5M-7J-77L" firstAttribute="centerX" secondItem="KQZ-1w-vlD" secondAttribute="centerX" id="e1l-AV-tCB"/>
<constraint firstItem="W20-aa-6Xr" firstAttribute="top" secondItem="Ia1-K6-d13" secondAttribute="bottom" constant="350" id="pab-Li-xZ5"/>
</constraints>
</view>
<tabBarItem key="tabBarItem" title="Select User" image="first" id="acW-dT-cKf"/>
<connections>
<outlet property="mainLabel" destination="KQZ-1w-vlD" id="fWK-H9-WXN"/>
<outlet property="signInButton" destination="25e-pE-eb8" id="ovt-t5-7BV"/>
<outlet property="signOutButton" destination="RKb-EE-mCZ" id="afy-Q6-rK8"/>
<outlet property="subLabel" destination="A5M-7J-77L" id="DIj-ZX-1NK"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="W5J-7L-Pyd" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="750" y="-320"/>
</scene>
<!--Make RPC-->
<scene sceneID="wg7-f3-ORb">
<objects>
<viewController id="8rJ-Kc-sve" userLabel="Make RPC" customClass="MakeRPCViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="L7p-HK-0SC"/>
<viewControllerLayoutGuide type="bottom" id="Djb-ko-YwX"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="QS5-Rx-YEW">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" text="Waiting for RPC to complete..." lineBreakMode="wordWrap" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="zEq-FU-wV5">
<rect key="frame" x="16" y="286" width="568" height="28"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Helvetica" family="Helvetica" pointSize="18"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="zEq-FU-wV5" firstAttribute="leading" secondItem="QS5-Rx-YEW" secondAttribute="leadingMargin" id="173-PJ-A9Y"/>
<constraint firstAttribute="trailingMargin" secondItem="zEq-FU-wV5" secondAttribute="trailing" id="Tct-jU-lL7"/>
<constraint firstAttribute="centerY" secondItem="zEq-FU-wV5" secondAttribute="centerY" id="qzY-Ky-pLD"/>
</constraints>
</view>
<tabBarItem key="tabBarItem" title="Make RPC" image="second" id="cPa-gy-q4n" userLabel="Make RPC"/>
<connections>
<outlet property="mainLabel" destination="zEq-FU-wV5" id="l1H-U6-oAa"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="4Nw-L8-lE0" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="750" y="360"/>
</scene>
<!--Tab Bar Controller-->
<scene sceneID="yl2-sM-qoP">
<objects>
<tabBarController id="49e-Tb-3d3" sceneMemberID="viewController">
<nil key="simulatedBottomBarMetrics"/>
<tabBar key="tabBar" contentMode="scaleToFill" id="W28-zg-YXA">
<rect key="frame" x="0.0" y="975" width="768" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tabBar>
<connections>
<segue destination="9pv-A4-QxB" kind="relationship" relationship="viewControllers" id="u7Y-xg-7CH"/>
<segue destination="8rJ-Kc-sve" kind="relationship" relationship="viewControllers" id="lzU-1b-eKA"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="0.0" y="0.0"/>
</scene>
</scenes>
<resources>
<image name="first" width="30" height="30"/>
<image name="second" width="30" height="30"/>
</resources>
</document>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLIENT_ID</key>
<string>15087385131-lh9bpkiai9nls53uadju0if6k7un3uih.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.15087385131-lh9bpkiai9nls53uadju0if6k7un3uih</string>
</dict>
</plist>

View File

@ -0,0 +1,68 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "first.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "second.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>io.grpc.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>REVERSED_CLIENT_ID</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.15087385131-lh9bpkiai9nls53uadju0if6k7un3uih</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>BUNDLE_ID</string>
<key>CFBundleURLSchemes</key>
<array>
<string>io.grpc.AuthSample</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>Main</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,41 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@ -0,0 +1,10 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
target 'AuthSample' do
# Depend on the generated AuthTestService library.
pod 'AuthTestService', :path => '.'
# Depend on Google's OAuth2 library
pod 'Google/SignIn'
end

View File

@ -0,0 +1,175 @@
#OAuth2 on gRPC: Objective-C
This example application demostrates how to use OAuth2 on gRPC to make authenticated API calls on
behalf of a user. By walking through it you'll learn how to use the Objective-C gRPC API to:
- Initialize and configure a remote call object before the RPC is started.
- Set request metadata elements on a call, which are semantically equivalent to HTTP request
headers.
- Read response metadata from a call, which is equivalent to HTTP response headers and trailers.
It assumes you know the basics on how to make gRPC API calls using the Objective-C client library,
as shown in the [Hello World](https://github.com/grpc/grpc-common/tree/master/objective-c/helloworld)
or [Route Guide](https://github.com/grpc/grpc-common/tree/master/objective-c/route_guide) tutorials,
and are familiar with OAuth2 concepts like _access token_.
- [Example code and setup](#setup)
- [Try it out!](#try)
- [Create an RPC object and start it later](#rpc-object)
- [Set request metadata of a call: Authorization header with an access token](#request-metadata)
- [Get response metadata of a call: Auth challenge header](#response-metadata)
<a name="setup"></a>
## Example code and setup
The example code for our tutorial is in [grpc/grpc-common/objective-c/auth_sample](https://github.com/grpc/grpc-common/tree/master/objective-c/auth_sample).
To download the example, clone the `grpc-common` repository by running the following command:
```shell
$ git clone https://github.com/grpc/grpc-common.git
```
Then change your current directory to `grpc-common/objective-c/auth_sample`:
```shell
$ cd grpc-common/objective-c/auth_sample
```
Our example is a simple application with two views. The first one lets a user sign in and out using
the OAuth2 flow of Google's [iOS SignIn library](https://developers.google.com/identity/sign-in/ios/).
(Google's library is used in this example because the test gRPC service we are going to call expects
Google account credentials, but neither gRPC nor the Objective-C client library is tied to any
specific OAuth2 provider). The second view makes a gRPC request to the test server, using the
access token obtained by the first view.
Note: OAuth2 libraries need the application to register and obtain an ID from the identity provider
(in the case of this example app, Google). The app's XCode project is configured using that ID, so
you shouldn't copy this project "as is" for your own app: it would result in your app being
identified in the consent screen as "gRPC-AuthSample", and not having access to real Google
services. Instead, configure your own XCode project following the [instructions here](https://developers.google.com/identity/sign-in/ios/).
As with the other examples, you also should have [Cocoapods](https://cocoapods.org/#install)
installed, as well as the relevant tools to generate the client library code. You can obtain the
latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc).
<a name="try"></a>
## Try it out!
To try the sample app, first have Cocoapods generate and install the client library for our .proto
files:
```shell
$ pod install
```
(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet
on your computer's cache).
Finally, open the XCode workspace created by Cocoapods, and run the app.
The first view, `SelectUserViewController.h/m`, asks you to sign in with your Google account, and to
give the "gRPC-AuthSample" app the following permissions:
- View your email address.
- View your basic profile info.
- "Test scope for access to the Zoo service".
This last permission, corresponding to the scope `https://www.googleapis.com/auth/xapi.zoo` doesn't
grant any real capability: it's only used for testing. You can log out at any time.
The second view, `MakeRPCViewController.h/m`, makes a gRPC request to a test server at
https://grpc-test.sandbox.google.com, sending the access token along with the request. The test
service simply validates the token and writes in its response which user it belongs to, and which
scopes it gives access to. (The client application already knows those two values; it's a way to
verify that everything went as expected).
The next sections guide you step-by-step through how the gRPC call in `MakeRPCViewController` is
performed.
<a name="rpc-object"></a>
## Create an RPC object and start it later
The other basic tutorials show how to invoke an RPC by calling an asynchronous method in a generated
client object. This shows how to initialize an object that represents the RPC, and configure it
before starting the network request.
Assume you have a proto service definition like this:
```protobuf
option objc_class_prefix = "AUTH";
service TestService {
rpc UnaryCall(Request) returns (Response);
}
```
A `unaryCallWithRequest:handler:` method, with which you're already familiar, is generated for the
`AUTHTestService` class:
```objective-c
[client unaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
...
}];
```
In addition, an `RPCToUnaryCallWithRequest:handler:` method is generated, which returns a
not-yet-started RPC object:
```objective-c
#import <gRPC/ProtoRPC.h>
ProtoRPC *call =
[client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
...
}];
```
The RPC represented by this object can be started at any later time like this:
```objective-c
[call start];
```
<a name="request-metadata"></a>
## Set request metadata of a call: Authorization header with an access token
The `ProtoRPC` class has a `requestMetadata` property defined like this:
```objective-c
@property(nonatomic, readwrite) NSMutableDictionary *requestMetadata;
```
Setting it to a dictionary of metadata keys and values will have them sent on the wire when the call
is started. gRPC metadata are pieces of information about the call sent by the client to the server
(and vice versa). They take the form of key-value pairs and are essentially opaque to gRPC itself.
```objective-c
call.requestMetadata = [NSMutableDictionary dictionaryWithDictionary:
@{@"My-Header": @"Value for this header",
@"Another-Header": @"Its value"}];
```
If you have an access token, OAuth2 specifies it is to be sent in this format:
```objective-c
@{@"Authorization": [@"Bearer " stringByAppendingString:accessToken]}
```
<a name="response-metadata"></a>
## Get response metadata of a call: Auth challenge header
The `ProtoRPC` class also has a `responseMetadata` property, analogous to the request metadata we
just looked at. It's defined like this:
```objective-c
@property(atomic, readonly) NSDictionary *responseMetadata;
```
Because gRPC metadata keys can be repeated, the values of the `responseMetadata` dictionary are
always `NSArray`s. Thus, to access OAuth2's authentication challenge header you write:
```objective-c
call.responseMetadata[@"www-authenticate"][0]
```
Note that, as gRPC metadata elements are mapped to HTTP/2 headers (or trailers), the keys of the
response metadata are always ASCII strings in lowercase.

View File

@ -0,0 +1,42 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <Google/SignIn.h>
#import <UIKit/UIKit.h>
@interface SelectUserViewController : UIViewController <GIDSignInDelegate, GIDSignInUIDelegate>
@property(weak, nonatomic) IBOutlet GIDSignInButton *signInButton;
@property(weak, nonatomic) IBOutlet UIButton *signOutButton;
@property(weak, nonatomic) IBOutlet UILabel *mainLabel;
@property(weak, nonatomic) IBOutlet UILabel *subLabel;
@end

View File

@ -0,0 +1,86 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "SelectUserViewController.h"
#import "MakeRPCViewController.h"
@implementation SelectUserViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.signOutButton.layer.cornerRadius = 5;
self.signOutButton.hidden = YES;
// As instructed in https://developers.google.com/identity/sign-in/ios/sign-in
GIDSignIn *signIn = GIDSignIn.sharedInstance;
signIn.delegate = self;
signIn.uiDelegate = self;
// As instructed in https://developers.google.com/identity/sign-in/ios/additional-scopes
if (![signIn.scopes containsObject:kTestScope]) {
signIn.scopes = [signIn.scopes arrayByAddingObject:kTestScope];
}
[signIn signInSilently];
}
- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error {
if (error) {
// The user probably cancelled the sign-in flow.
return;
}
self.mainLabel.text = [NSString stringWithFormat:@"User: %@", user.profile.email];
NSString *scopes = [user.accessibleScopes componentsJoinedByString:@", "];
scopes = scopes.length ? scopes : @"(none)";
self.subLabel.text = [NSString stringWithFormat:@"Scopes: %@", scopes];
self.signInButton.hidden = YES;
self.signOutButton.hidden = NO;
}
- (IBAction)didTapSignOut {
[GIDSignIn.sharedInstance signOut];
self.mainLabel.text = @"Please sign in.";
self.subLabel.text = @"";
self.signInButton.hidden = NO;
self.signOutButton.hidden = YES;
}
@end

57
protos/auth_sample.proto Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package grpc.testing;
option objc_class_prefix = "AUTH";
// Unary request.
message Request {
// Whether Response should include username.
bool fill_username = 4;
// Whether Response should include OAuth scope.
bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
message Response {
// The user the request came from, for verifying authentication was
// successful.
string username = 2;
// OAuth scope.
string oauth_scope = 3;
}
service TestService {
// One request followed by one response.
rpc UnaryCall(Request) returns (Response);
}