grpc.io/public/docs/tutorials/auth/oauth2-objective-c/index.html

342 lines
20 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<title>
OAuth2 on gRPC - Objective-C &ndash; gRPC
</title>
<link rel="apple-touch-icon" href="/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" type="image/png" href="/favicons/android-chrome-192x192.png" sizes="192x192" >
<link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="/favicons/manifest.json">
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#2DA6B0">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/favicons/mstile-150x150.png">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-60127042-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-60127042-1');
</script>
</head>
<body>
<div id="landing-content">
<div class="row">
<div class="topbannersub">
<nav class="navbar navbar-expand-md navbar-dark topnav">
<a class="navbar-brand" href="https://cjyabraham.github.io/">
<img src="https://cjyabraham.github.io/img/grpc-logo.png" width="114" height="50">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="topnav, collapse navbar-collapse" id="navbarSupportedContent" style="float:right !important">
<ul class="navbar-nav ml-auto">
<li class="nav-item ">
<a class="nav-link" href="https://cjyabraham.github.io/about/">About</a>
</li>
<li class="nav-item dropdown active">
<a class="nav-link dropdown-toggle" href="https://cjyabraham.github.io/docs/" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Docs
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="/docs">
Overview
</a>
<a class="dropdown-item" href="/docs/quickstart/">
Quick Start
</a>
<a class="dropdown-item" href="/docs/guides/">
Guides
</a>
<a class="dropdown-item" href="/docs/tutorials/">
Tutorials
</a>
<a class="dropdown-item" href="/docs/reference/">
Reference
</a>
<a class="dropdown-item" href="/docs/samples/">
Samples
</a>
<a class="dropdown-item" href="/docs/talks">
Presentations
</a>
</div>
</li>
<li class="nav-item ">
<a class="nav-link" href="/blog">
Blog
</a>
</li>
<li class="nav-item ">
<a class="nav-link" href="/community">Community</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://packages.grpc.io/">
Packages
</a>
</li>
<li class="nav-item ">
<a class="nav-link" href="https://cjyabraham.github.io/faq/">FAQ</a>
</li>
</ul>
</div>
</nav>
<div class="headertext">Documentation</div>
</div>
</div>
</div>
<div class="subnav d-none d-md-block">
<a href="https://cjyabraham.github.io/docs/" >Overview</a>
| <a href="https://cjyabraham.github.io/docs/quickstart/" >Quick Start</a>
| <a href="https://cjyabraham.github.io/docs/guides/" >Guides</a>
| <a href="https://cjyabraham.github.io/docs/tutorials/" class="active">Tutorials</a>
| <a href="https://cjyabraham.github.io/docs/reference/" >Reference</a>
| <a href="https://cjyabraham.github.io/docs/samples/" >Samples</a>
| <a href="https://cjyabraham.github.io/docs/talks/" >Presentations</a>
</div>
<div class="quickstartcols">
<div class="quickstartcol1">
<h8>Tutorials</h8>
<a href="https://cjyabraham.github.io/docs/tutorials/async/helloasync-cpp/" >Async - C++</a>
<a href="https://cjyabraham.github.io/docs/tutorials/auth/oauth2-objective-c/" class="active">Auth - Objective C</a>
<h8>Basic</h8>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/c/" >C++</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/csharp/" >C#</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/dart/" >Dart</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/go/" >Go</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/java/" >Java</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/android/" >Android Java</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/node/" >Node</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/objective-c/" class="active">Objective-C</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/php/" >PHP</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/python/" >Python</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/ruby/" >Ruby</a>
<a href="https://cjyabraham.github.io/docs/tutorials/basic/web/" >Web</a>
</div>
<div class="quickstartcol2" style="margin-top:4%">
<h3 style="margin-top:0px;">OAuth2 on gRPC - Objective-C</h3>
<p>This example demonstrates how to use OAuth2 on gRPC to make
authenticated API calls on behalf of a user.</p>
<p>By walking through it you&rsquo;ll also learn how to use the Objective-C gRPC API to:</p>
<ul>
<li>Initialize and configure a remote call object before the RPC is started.</li>
<li>Set request metadata elements on a call, which are semantically equivalent to
HTTP request headers.</li>
<li>Read response metadata from a call, which is equivalent to HTTP response
headers and trailers.</li>
</ul>
<p>It assumes you know the basics on how to make gRPC API calls using the
Objective-C client library, as shown in <a href="/docs/tutorials/basic/objective-c/">gRPC Basics:
Objective-C</a> and the
<a href="/docs/">overview</a>, and are familiar with OAuth2 concepts like <em>access
token</em>.</p>
<div id="toc"></div>
<p><a name="setup"></a></p>
<h3 id="example-code-and-setup">Example code and setup</h3>
<p>The example code for our tutorial is in
<a href="https://github.com/grpc/grpc/tree/
v1.20.0/examples/objective-c/auth_sample">gprc/examples/objective-c/auth_sample</a>. To
download the example, clone this repository by running the following commands:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ git clone -b v1.20.0 https://github.com/grpc/grpc
$ cd grpc
$ git submodule update --init</code></pre></div>
<p>Then change your current directory to <code>examples/objective-c/auth_sample</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ cd examples/objective-c/auth_sample</code></pre></div>
<p>Our example is a simple application with two views. The first view lets a user
sign in and out using the OAuth2 flow of Google&rsquo;s <a href="https://developers.google.com/identity/sign-in/ios/">iOS SignIn
library</a>. (Google&rsquo;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.</p>
<p>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&rsquo;s XCode
project is configured using that ID, so you shouldn&rsquo;t copy this project &ldquo;as is&rdquo;
for your own app: it would result in your app being identified in the consent
screen as &ldquo;gRPC-AuthSample&rdquo;, and not having access to real Google services.
Instead, configure your own XCode project following the <a href="https://developers.google.com/identity/sign-in/ios/">instructions
here</a>.</p>
<p>As with the other Objective-C examples, you also should have
<a href="https://cocoapods.org/#install">Cocoapods</a> installed, as well as the relevant
tools to generate the client library code. You can obtain the latter by
following <a href="https://github.com/grpc/homebrew-grpc">these setup instructions</a>.</p>
<p><a name="try"></a></p>
<h3 id="try-it-out">Try it out!</h3>
<p>To try the sample app, first have Cocoapods generate and install the client library for our .proto
files:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ pod install</code></pre></div>
<p>(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods
doesn&rsquo;t have it yet on your computer&rsquo;s cache).</p>
<p>Finally, open the XCode workspace created by Cocoapods, and run the app.</p>
<p>The first view, <code>SelectUserViewController.h/m</code>, asks you to sign in with your
Google account, and to give the &ldquo;gRPC-AuthSample&rdquo; app the following permissions:</p>
<ul>
<li>View your email address.</li>
<li>View your basic profile info.</li>
<li>&ldquo;Test scope for access to the Zoo service&rdquo;.</li>
</ul>
<p>This last permission, corresponding to the scope
<code>https://www.googleapis.com/auth/xapi.zoo</code> doesn&rsquo;t grant any real capability:
it&rsquo;s only used for testing. You can log out at any time.</p>
<p>The second view, <code>MakeRPCViewController.h/m</code>, makes a gRPC request to a test
server at <a href="https://grpc-test.sandbox.google.com">https://grpc-test.sandbox.google.com</a>, 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&rsquo;s a way to verify that
everything went as expected).</p>
<p>The next sections guide you step-by-step through how the gRPC call in
<code>MakeRPCViewController</code> is performed. You can see the complete code in
<a href="https://github.com/grpc/grpc/blob/v1.20.0/examples/objective-c/auth_sample/MakeRPCViewController.m">MakeRPCViewController.m</a>.</p>
<p><a name="rpc-object"></a></p>
<h3 id="create-an-rpc-object">Create an RPC object</h3>
<p>The other basic tutorials show how to invoke an RPC by calling an asynchronous
method in a generated client object. However, to make an authenticated call you
need to initialize an object that represents the RPC, and configure it <em>before</em>
starting the network request. First let&rsquo;s look at how to create the RPC object.</p>
<p>Assume you have a proto service definition like this:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#66d9ef">option</span> objc_class_prefix <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;AUTH&#34;</span>;<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">service</span> TestService {<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#66d9ef">rpc</span> UnaryCall(Request) <span style="color:#66d9ef">returns</span> (Response);<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span>}</code></pre></div>
<p>A <code>unaryCallWithRequest:handler:</code> method, with which you&rsquo;re already familiar, is
generated for the <code>AUTHTestService</code> class:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c">[client unaryCallWithRequest:request handler:<span style="color:#f92672">^</span>(AUTHResponse <span style="color:#f92672">*</span>response, NSError <span style="color:#f92672">*</span>error) {
...
}];</code></pre></div>
<p>In addition, an <code>RPCToUnaryCallWithRequest:handler:</code> method is generated, which returns a
not-yet-started RPC object:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c"><span style="color:#75715e">#import &lt;ProtoRPC/ProtoRPC.h&gt;
</span><span style="color:#75715e"></span>
ProtoRPC <span style="color:#f92672">*</span>call <span style="color:#f92672">=</span>
[client RPCToUnaryCallWithRequest:request handler:<span style="color:#f92672">^</span>(AUTHResponse <span style="color:#f92672">*</span>response, NSError <span style="color:#f92672">*</span>error) {
...
}];</code></pre></div>
<p>You can start the RPC represented by this object at any later time like this:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c">[call start];</code></pre></div>
<p><a name="request-metadata"></a></p>
<h3 id="setting-request-metadata-auth-header-with-an-access-token">Setting request metadata: Auth header with an access token</h3>
<p>Now let&rsquo;s look at how to configure some settings on the RPC object. The
<code>ProtoRPC</code> class has a <code>requestHeaders</code> property (inherited from <code>GRPCCall</code>)
defined like this:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c"><span style="color:#66d9ef">@property</span>(<span style="color:#66d9ef">atomic</span>, <span style="color:#66d9ef">readonly</span>) <span style="color:#66d9ef">id</span><span style="color:#f92672">&lt;</span>GRPCRequestHeaders<span style="color:#f92672">&gt;</span> requestHeaders</code></pre></div>
<p>You can think of the <code>GRPCRequestHeaders</code> protocol as equivalent to the
<code>NSMutableDictionary</code> class. Setting elements of this dictionary of metadata
keys and values means this metadata will be 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.</p>
<p>For convenience, the property is initialized with an empty
<code>NSMutableDictionary</code>, so that request metadata elements can be set like this:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c">call.requestHeaders[<span style="color:#e6db74">@&#34;My-Header&#34;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">@&#34;Value for this header&#34;</span>;
call.requestHeaders[<span style="color:#e6db74">@&#34;Another-Header&#34;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">@&#34;Its value&#34;</span>;</code></pre></div>
<p>A typical use of metadata is for authentication details, as in our example. If
you have an access token, OAuth2 specifies it is to be sent in this format:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c">call.requestHeaders[<span style="color:#e6db74">@&#34;Authorization&#34;</span>] <span style="color:#f92672">=</span> [<span style="color:#e6db74">@&#34;Bearer &#34;</span> stringByAppendingString:accessToken];</code></pre></div>
<p><a name="response-metadata"></a></p>
<h3 id="getting-response-metadata-auth-challenge-header">Getting response metadata: Auth challenge header</h3>
<p>The <code>ProtoRPC</code> class also inherits a pair of properties, <code>responseHeaders</code> and
<code>responseTrailers</code>, analogous to the request metadata we just looked at but sent
back by the server to the client. They are defined like this:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c"><span style="color:#66d9ef">@property</span>(<span style="color:#66d9ef">atomic</span>, <span style="color:#66d9ef">readonly</span>) NSDictionary <span style="color:#f92672">*</span>responseHeaders;
<span style="color:#66d9ef">@property</span>(<span style="color:#66d9ef">atomic</span>, <span style="color:#66d9ef">readonly</span>) NSDictionary <span style="color:#f92672">*</span>responseTrailers;</code></pre></div>
<p>In OAuth2, if there&rsquo;s an authentication error the server will send back a
challenge header. This is returned in the RPC&rsquo;s response headers. To access
this, as in our example&rsquo;s error-handling code, you write:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c">call.responseHeaders[<span style="color:#e6db74">@&#34;www-authenticate&#34;</span>]</code></pre></div>
<p>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.</p>
<p>Many uses cases of response metadata involve getting more details about an RPC
error. For convenience, when a <code>NSError</code> instance is passed to an RPC handler
block, the response headers and trailers dictionaries can also be accessed this
way:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-objective-c" data-lang="objective-c">error.userInfo[kGRPCHeadersKey] <span style="color:#f92672">==</span> call.responseHeaders
error.userInfo[kGRPCTrailersKey] <span style="color:#f92672">==</span> call.responseTrailers</code></pre></div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>