diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index 2952dcd36a98577b6b1d93cba4564d37e4a5fd91..5426b47c76185f75774e6006ae5125b1c08fd708 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -776,6 +776,42 @@ class ServicerContext(six.with_metaclass(abc.ABCMeta, RpcContext)):
     """
         raise NotImplementedError()
 
+    @abc.abstractmethod
+    def peer_identities(self):
+        """Gets one or more peer identity(s).
+
+      Equivalent to
+      servicer_context.auth_context().get(
+          servicer_context.peer_identity_key())
+
+    Returns:
+      An iterable of the identities, or None if the call is not authenticated.
+      Each identity is returned as a raw bytes type.
+     """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def peer_identity_key(self):
+        """The auth property used to identify the peer.
+
+    For example, "x509_common_name" or "x509_subject_alternative_name" are
+    used to identify an SSL peer.
+
+    Returns:
+      The auth property (string) that indicates the
+      peer identity, or None if the call is not authenticated.
+    """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def auth_context(self):
+        """Gets the auth context for the call.
+
+      Returns:
+        A map of strings to an iterable of bytes for each auth property.
+      """
+        raise NotImplementedError()
+
     @abc.abstractmethod
     def send_initial_metadata(self, initial_metadata):
         """Sends the initial metadata value to the client.
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 1db2056d476a70394eb5cb49f46307dc5d526158..e71d3e7dc1c1658f1d02ee8613f7f4de75b6f47f 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -486,6 +486,35 @@ cdef extern from "grpc/grpc_security.h":
   grpc_call_credentials *grpc_metadata_credentials_create_from_plugin(
       grpc_metadata_credentials_plugin plugin, void *reserved) nogil
 
+  ctypedef struct grpc_auth_property_iterator:
+    pass
+
+  ctypedef struct grpc_auth_property:
+    char *name
+    char *value
+    size_t value_length
+
+  grpc_auth_property *grpc_auth_property_iterator_next(
+      grpc_auth_property_iterator *it)
+
+  grpc_auth_property_iterator grpc_auth_context_property_iterator(
+      const grpc_auth_context *ctx)
+ 
+  grpc_auth_property_iterator grpc_auth_context_peer_identity(
+      const grpc_auth_context *ctx)
+
+  char *grpc_auth_context_peer_identity_property_name(
+      const grpc_auth_context *ctx)
+
+  grpc_auth_property_iterator grpc_auth_context_find_properties_by_name(
+      const grpc_auth_context *ctx, const char *name)
+
+  grpc_auth_context_peer_is_authenticated(
+      const grpc_auth_context *ctx)
+
+  grpc_auth_context *grpc_call_auth_context(grpc_call *call)
+
+  void grpc_auth_context_release(grpc_auth_context *context)
 
 cdef extern from "grpc/compression.h":
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
index 357b0330d5ce7c9295f74d298dbed33c8de79c4d..a21eac79951004f3946e8afae17a9d2e18568dc7 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
@@ -44,3 +44,61 @@ cdef grpc_ssl_roots_override_result ssl_roots_override_callback(
     pem_root_certs[0][len(temporary_pem_root_certs)] = '\0'
 
   return GRPC_SSL_ROOTS_OVERRIDE_OK
+
+
+def peer_identities(Call call):
+  cdef grpc_auth_context* auth_context
+  cdef grpc_auth_property_iterator properties
+  cdef grpc_auth_property* property
+
+  auth_context = grpc_call_auth_context(call.c_call)
+  if auth_context == NULL:
+    return None
+  properties = grpc_auth_context_peer_identity(auth_context)
+  identities = []
+  while True:
+    property = grpc_auth_property_iterator_next(&properties)
+    if property == NULL:
+      break
+    if property.value != NULL:
+      identities.append(<bytes>(property.value))
+  grpc_auth_context_release(auth_context)
+  return identities if identities else None
+
+def peer_identity_key(Call call):
+  cdef grpc_auth_context* auth_context
+  cdef char* c_key
+  auth_context = grpc_call_auth_context(call.c_call)
+  if auth_context == NULL:
+    return None
+  c_key = grpc_auth_context_peer_identity_property_name(auth_context)
+  if c_key == NULL:
+    key = None
+  else:
+    key = <bytes> grpc_auth_context_peer_identity_property_name(auth_context)
+  grpc_auth_context_release(auth_context)
+  return key
+
+def auth_context(Call call):
+  cdef grpc_auth_context* auth_context
+  cdef grpc_auth_property_iterator properties
+  cdef grpc_auth_property* property
+
+  auth_context = grpc_call_auth_context(call.c_call)
+  if auth_context == NULL:
+    return {}
+  properties = grpc_auth_context_property_iterator(auth_context)
+  py_auth_context = {}
+  while True:
+    property = grpc_auth_property_iterator_next(&properties)
+    if property == NULL:
+      break
+    if property.name != NULL and property.value != NULL:
+      key = <bytes> property.name
+      if key in py_auth_context:
+        py_auth_context[key].append(<bytes>(property.value))
+      else:
+        py_auth_context[key] = [<bytes> property.value]
+  grpc_auth_context_release(auth_context)
+  return py_auth_context
+  
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index f29c44a4cfd3e35b9dfbc01b08f2cfe882423172..860085f0c7ee0ebde58cafa1768fb8912e262872 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -31,6 +31,7 @@
 import collections
 import enum
 import logging
+import six
 import threading
 import time
 
@@ -255,6 +256,20 @@ class _Context(grpc.ServicerContext):
     def peer(self):
         return _common.decode(self._rpc_event.operation_call.peer())
 
+    def peer_identities(self):
+        return cygrpc.peer_identities(self._rpc_event.operation_call)
+
+    def peer_identity_key(self):
+        id_key = cygrpc.peer_identity_key(self._rpc_event.operation_call)
+        return id_key if id_key is None else _common.decode(id_key)
+
+    def auth_context(self):
+        return {
+            _common.decode(key): value
+            for key, value in six.iteritems(
+                cygrpc.auth_context(self._rpc_event.operation_call))
+        }
+
     def send_initial_metadata(self, initial_metadata):
         with self._state.condition:
             if self._state.client is _CANCELLED:
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index f750b0510220ee7078896ba7f88503617c1cdbb5..9f7587faa667e4ab460dd1bb27e8709a778f34ea 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -12,6 +12,7 @@
   "unit._api_test.AllTest",
   "unit._api_test.ChannelConnectivityTest",
   "unit._api_test.ChannelTest",
+  "unit._auth_context_test.AuthContextTest",
   "unit._auth_test.AccessTokenCallCredentialsTest",
   "unit._auth_test.GoogleCallCredentialsTest",
   "unit._channel_args_test.ChannelArgsTest",
diff --git a/src/python/grpcio_tests/tests/unit/_auth_context_test.py b/src/python/grpcio_tests/tests/unit/_auth_context_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..fce6ac967dbd57890a5dbb07c22d6738b443aa22
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_auth_context_test.py
@@ -0,0 +1,154 @@
+# Copyright 2017, 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.
+"""Tests exposure of SSL auth context"""
+
+import pickle
+import unittest
+
+import grpc
+from grpc import _channel
+from grpc.framework.foundation import logging_pool
+import six
+
+from tests.unit import test_common
+from tests.unit.framework.common import test_constants
+from tests.unit import resources
+
+_REQUEST = b'\x00\x00\x00'
+_RESPONSE = b'\x00\x00\x00'
+
+_UNARY_UNARY = '/test/UnaryUnary'
+
+_SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
+_CLIENT_IDS = (b'*.test.google.fr', b'waterzooi.test.google.be',
+               b'*.test.youtube.com', b'192.168.1.3',)
+_ID = 'id'
+_ID_KEY = 'id_key'
+_AUTH_CTX = 'auth_ctx'
+
+_PRIVATE_KEY = resources.private_key()
+_CERTIFICATE_CHAIN = resources.certificate_chain()
+_TEST_ROOT_CERTIFICATES = resources.test_root_certificates()
+_SERVER_CERTS = ((_PRIVATE_KEY, _CERTIFICATE_CHAIN),)
+_PROPERTY_OPTIONS = (('grpc.ssl_target_name_override', _SERVER_HOST_OVERRIDE,),)
+
+
+def handle_unary_unary(request, servicer_context):
+    return pickle.dumps({
+        _ID: servicer_context.peer_identities(),
+        _ID_KEY: servicer_context.peer_identity_key(),
+        _AUTH_CTX: servicer_context.auth_context()
+    })
+
+
+class AuthContextTest(unittest.TestCase):
+
+    def testInsecure(self):
+        server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
+        handler = grpc.method_handlers_generic_handler('test', {
+            'UnaryUnary':
+            grpc.unary_unary_rpc_method_handler(handle_unary_unary)
+        })
+        server = grpc.server(server_pool, (handler,))
+        port = server.add_insecure_port('[::]:0')
+        server.start()
+
+        channel = grpc.insecure_channel('localhost:%d' % port)
+        response = channel.unary_unary(_UNARY_UNARY)(_REQUEST)
+        server.stop(None)
+
+        auth_data = pickle.loads(response)
+        self.assertIsNone(auth_data[_ID])
+        self.assertIsNone(auth_data[_ID_KEY])
+        self.assertDictEqual({}, auth_data[_AUTH_CTX])
+
+    def testSecureNoCert(self):
+        server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
+        handler = grpc.method_handlers_generic_handler('test', {
+            'UnaryUnary':
+            grpc.unary_unary_rpc_method_handler(handle_unary_unary)
+        })
+        server = grpc.server(server_pool, (handler,))
+        server_cred = grpc.ssl_server_credentials(_SERVER_CERTS)
+        port = server.add_secure_port('[::]:0', server_cred)
+        server.start()
+
+        channel_creds = grpc.ssl_channel_credentials(
+            root_certificates=_TEST_ROOT_CERTIFICATES)
+        channel = grpc.secure_channel(
+            'localhost:{}'.format(port),
+            channel_creds,
+            options=_PROPERTY_OPTIONS)
+        response = channel.unary_unary(_UNARY_UNARY)(_REQUEST)
+        server.stop(None)
+
+        auth_data = pickle.loads(response)
+        self.assertIsNone(auth_data[_ID])
+        self.assertIsNone(auth_data[_ID_KEY])
+        self.assertDictEqual({
+            'transport_security_type': [b'ssl']
+        }, auth_data[_AUTH_CTX])
+
+    def testSecureClientCert(self):
+        server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
+        handler = grpc.method_handlers_generic_handler('test', {
+            'UnaryUnary':
+            grpc.unary_unary_rpc_method_handler(handle_unary_unary)
+        })
+        server = grpc.server(server_pool, (handler,))
+        server_cred = grpc.ssl_server_credentials(
+            _SERVER_CERTS,
+            root_certificates=_TEST_ROOT_CERTIFICATES,
+            require_client_auth=True)
+        port = server.add_secure_port('[::]:0', server_cred)
+        server.start()
+
+        channel_creds = grpc.ssl_channel_credentials(
+            root_certificates=_TEST_ROOT_CERTIFICATES,
+            private_key=_PRIVATE_KEY,
+            certificate_chain=_CERTIFICATE_CHAIN)
+        channel = grpc.secure_channel(
+            'localhost:{}'.format(port),
+            channel_creds,
+            options=_PROPERTY_OPTIONS)
+
+        response = channel.unary_unary(_UNARY_UNARY)(_REQUEST)
+        server.stop(None)
+
+        auth_data = pickle.loads(response)
+        auth_ctx = auth_data[_AUTH_CTX]
+        six.assertCountEqual(self, _CLIENT_IDS, auth_data[_ID])
+        self.assertEqual('x509_subject_alternative_name', auth_data[_ID_KEY])
+        self.assertSequenceEqual([b'ssl'], auth_ctx['transport_security_type'])
+        self.assertSequenceEqual([b'*.test.google.com'],
+                                 auth_ctx['x509_common_name'])
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)