Merge pull request #243 from jcanizales/add-objc-auth-demo
Add objc auth demo
This commit is contained in:
commit
1cbf744b58
|
|
@ -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).
|
||||||
|
|
|
||||||
|
|
@ -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 */;
|
||||||
|
}
|
||||||
7
objective-c/auth_sample/AuthSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
objective-c/auth_sample/AuthSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:AuthSample.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "first.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "second.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -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>
|
||||||
|
|
@ -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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue