examples: Add a JWT authentication example (#5915)

diff --git a/RELEASING.md b/RELEASING.md
index 1d6ee0e..b3d262a 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -45,6 +45,8 @@
   examples/example-alts/build.gradle
   examples/example-gauth/build.gradle
   examples/example-gauth/pom.xml
+  examples/example-jwt-auth/build.gradle
+  examples/example-jwt-auth/pom.xml
   examples/example-hostname/build.gradle
   examples/example-hostname/pom.xml
   examples/example-kotlin/build.gradle
diff --git a/examples/README.md b/examples/README.md
index 878da52..fdcec2d 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -156,6 +156,8 @@
 
 - [Google Authentication](example-gauth)
 
+- [JWT-based Authentication](example-jwt-auth)
+
 - [Kotlin examples](example-kotlin)
 
 - [Kotlin Android examples](example-kotlin/android)
diff --git a/examples/example-jwt-auth/README.md b/examples/example-jwt-auth/README.md
new file mode 100644
index 0000000..ba49713
--- /dev/null
+++ b/examples/example-jwt-auth/README.md
@@ -0,0 +1,70 @@
+Authentication Example
+==============================================
+
+This example illustrates a simple JWT-based authentication implementation in gRPC using
+ server interceptor. It uses the JJWT library to create and verify JSON Web Tokens (JWTs).
+
+The example requires grpc-java to be pre-built. Using a release tag will download the relevant binaries
+from a maven repository. But if you need the latest SNAPSHOT binaries you will need to follow
+[COMPILING](../../COMPILING.md) to build these.
+
+The source code is [here](src/main/java/io/grpc/examples/jwtauth). 
+To build the example, run in this directory:
+```
+$ ../gradlew installDist
+```
+The build creates scripts `auth-server` and `auth-client` in the `build/install/example-jwt-auth/bin/` directory 
+which can be used to run this example. The example requires the server to be running before starting the
+client.
+
+Running auth-server is similar to the normal hello world example and there are no arguments to supply:
+
+**auth-server**:
+
+The auth-server accepts optional argument for port on which the server should run:
+
+```text
+USAGE: auth-server [port]
+```
+
+The auth-client accepts optional arguments for server-host, server-port, user-name and client-id:
+
+**auth-client**:
+
+```text
+USAGE: auth-client [server-host [server-port [user-name [client-id]]]]
+```
+
+The `user-name` value is simply passed in the `HelloRequest` message as payload and the value of
+`client-id` is included in the JWT claims passed in the metadata header.
+
+
+#### How to run the example:
+
+```bash
+# Run the server:
+./build/install/example-jwt-auth/bin/auth-server 50051
+# In another terminal run the client
+./build/install/example-jwt-auth/bin/auth-client localhost 50051 userA clientB
+```
+
+That's it! The client will show the user-name reflected back in the message from the server as follows:
+```
+INFO: Greeting: Hello, userA
+```
+
+And on the server side you will see the message with the client's identifier:
+```
+Processing request from clientB
+```
+
+## Maven
+
+If you prefer to use Maven follow these [steps](../README.md#maven). You can run the example as follows:
+
+```
+$ # Run the server
+$ mvn exec:java -Dexec.mainClass=io.grpc.examples.authentication.AuthServer -Dexec.args="50051"
+$ # In another terminal run the client
+$ mvn exec:java -Dexec.mainClass=io.grpc.examples.authentication.AuthClient -Dexec.args="localhost 50051 userA clientB"
+```
diff --git a/examples/example-jwt-auth/build.gradle b/examples/example-jwt-auth/build.gradle
new file mode 100644
index 0000000..8b2b576
--- /dev/null
+++ b/examples/example-jwt-auth/build.gradle
@@ -0,0 +1,84 @@
+plugins {
+    // Provide convenience executables for trying out the examples.
+    id 'application'
+    // ASSUMES GRADLE 2.12 OR HIGHER. Use plugin version 0.7.5 with earlier gradle versions
+    id 'com.google.protobuf' version '0.8.8'
+    // Generate IntelliJ IDEA's .idea & .iml project files
+    id 'idea'
+}
+
+repositories {
+    maven { // The google mirror is less flaky than mavenCentral()
+        url "https://maven-central.storage-download.googleapis.com/repos/central/data/"
+    }
+    mavenLocal()
+}
+
+sourceCompatibility = 1.7
+targetCompatibility = 1.7
+
+// IMPORTANT: You probably want the non-SNAPSHOT version of gRPC. Make sure you
+// are looking at a tagged version of the example and not "master"!
+
+// Feel free to delete the comment at the next line. It is just for safely
+// updating the version in our release process.
+def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION
+def protobufVersion = '3.11.0'
+def protocVersion = protobufVersion
+
+dependencies {
+    implementation "io.grpc:grpc-protobuf:${grpcVersion}"
+    implementation "io.grpc:grpc-stub:${grpcVersion}"
+    implementation "io.jsonwebtoken:jjwt:0.9.1"
+    implementation "javax.xml.bind:jaxb-api:2.3.1"
+
+    compileOnly "javax.annotation:javax.annotation-api:1.2"
+
+    runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
+
+    testImplementation "io.grpc:grpc-testing:${grpcVersion}"
+    testImplementation "junit:junit:4.12"
+    testImplementation "org.mockito:mockito-core:2.28.2"
+}
+
+protobuf {
+    protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
+    plugins {
+        grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
+    }
+    generateProtoTasks {
+        all()*.plugins { grpc {} }
+    }
+}
+
+// Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code.
+sourceSets {
+    main {
+        java {
+            srcDirs 'build/generated/source/proto/main/grpc'
+            srcDirs 'build/generated/source/proto/main/java'
+        }
+    }
+}
+
+startScripts.enabled = false
+
+task hellowWorldJwtAuthServer(type: CreateStartScripts) {
+    mainClassName = 'io.grpc.examples.jwtauth.AuthServer'
+    applicationName = 'auth-server'
+    outputDir = new File(project.buildDir, 'tmp')
+    classpath = startScripts.classpath
+}
+
+task hellowWorldJwtAuthClient(type: CreateStartScripts) {
+    mainClassName = 'io.grpc.examples.jwtauth.AuthClient'
+    applicationName = 'auth-client'
+    outputDir = new File(project.buildDir, 'tmp')
+    classpath = startScripts.classpath
+}
+
+applicationDistribution.into('bin') {
+    from(hellowWorldJwtAuthServer)
+    from(hellowWorldJwtAuthClient)
+    fileMode = 0755
+}
diff --git a/examples/example-jwt-auth/pom.xml b/examples/example-jwt-auth/pom.xml
new file mode 100644
index 0000000..a40481b
--- /dev/null
+++ b/examples/example-jwt-auth/pom.xml
@@ -0,0 +1,136 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>io.grpc</groupId>
+  <artifactId>example-jwt-auth</artifactId>
+  <packaging>jar</packaging>
+  <!-- Feel free to delete the comment at the end of these lines. It is just
+       for safely updating the version in our release process. -->
+  <version>1.29.0-SNAPSHOT</version><!-- CURRENT_GRPC_VERSION -->
+  <name>example-jwt-auth</name>
+  <url>https://github.com/grpc/grpc-java</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <grpc.version>1.29.0-SNAPSHOT</grpc.version><!-- CURRENT_GRPC_VERSION -->
+    <protobuf.version>3.11.0</protobuf.version>
+    <protoc.version>3.11.0</protoc.version>
+    <!-- required for jdk9 -->
+    <maven.compiler.source>1.7</maven.compiler.source>
+    <maven.compiler.target>1.7</maven.compiler.target>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>io.grpc</groupId>
+        <artifactId>grpc-bom</artifactId>
+        <version>${grpc.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.grpc</groupId>
+      <artifactId>grpc-netty-shaded</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.grpc</groupId>
+      <artifactId>grpc-protobuf</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.grpc</groupId>
+      <artifactId>grpc-stub</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.jsonwebtoken</groupId>
+      <artifactId>jjwt</artifactId>
+      <version>0.9.1</version>
+    </dependency>
+    <dependency>
+      <groupId>javax.xml.bind</groupId>
+      <artifactId>jaxb-api</artifactId>
+      <version>2.3.1</version>
+    </dependency>
+    <dependency>
+      <groupId>javax.annotation</groupId>
+      <artifactId>javax.annotation-api</artifactId>
+      <version>1.2</version>
+      <scope>provided</scope> <!-- not needed at runtime -->
+    </dependency>
+    <dependency>
+      <groupId>io.grpc</groupId>
+      <artifactId>grpc-testing</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>2.28.2</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <extensions>
+      <extension>
+        <groupId>kr.motd.maven</groupId>
+        <artifactId>os-maven-plugin</artifactId>
+        <version>1.5.0.Final</version>
+      </extension>
+    </extensions>
+    <plugins>
+      <plugin>
+        <groupId>org.xolstice.maven.plugins</groupId>
+        <artifactId>protobuf-maven-plugin</artifactId>
+        <version>0.5.1</version>
+        <configuration>
+          <protocArtifact>
+            com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
+          </protocArtifact>
+          <pluginId>grpc-java</pluginId>
+          <pluginArtifact>
+            io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
+          </pluginArtifact>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>compile</goal>
+              <goal>compile-custom</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>1.4.1</version>
+        <executions>
+          <execution>
+            <id>enforce</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <requireUpperBoundDeps/>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/examples/example-jwt-auth/settings.gradle b/examples/example-jwt-auth/settings.gradle
new file mode 100644
index 0000000..59ef05d
--- /dev/null
+++ b/examples/example-jwt-auth/settings.gradle
@@ -0,0 +1,8 @@
+pluginManagement {
+    repositories {
+        maven { // The google mirror is less flaky than mavenCentral()
+            url "https://maven-central.storage-download.googleapis.com/repos/central/data/"
+        }
+        gradlePluginPortal()
+    }
+}
diff --git a/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/AuthClient.java b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/AuthClient.java
new file mode 100644
index 0000000..f6ea4c5
--- /dev/null
+++ b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/AuthClient.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2019 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.examples.jwtauth;
+
+import io.grpc.CallCredentials;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import io.grpc.examples.helloworld.GreeterGrpc;
+import io.grpc.examples.helloworld.HelloReply;
+import io.grpc.examples.helloworld.HelloRequest;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+/**
+ * An authenticating client that requests a greeting from the {@link AuthServer}.
+ */
+public class AuthClient {
+
+  private static final Logger logger = Logger.getLogger(AuthClient.class.getName());
+
+  private final ManagedChannel channel;
+  private final GreeterGrpc.GreeterBlockingStub blockingStub;
+  private final CallCredentials callCredentials;
+
+  /**
+   * Construct client for accessing GreeterGrpc server.
+   */
+  AuthClient(CallCredentials callCredentials, String host, int port) {
+    this(
+        callCredentials,
+        ManagedChannelBuilder
+            .forAddress(host, port)
+            // Channels are secure by default (via SSL/TLS). For this example we disable TLS
+            // to avoid needing certificates, but it is recommended to use a secure channel
+            // while passing credentials.
+            .usePlaintext()
+            .build());
+  }
+
+  /**
+   * Construct client for accessing GreeterGrpc server using the existing channel.
+   */
+  AuthClient(CallCredentials callCredentials, ManagedChannel channel) {
+    this.callCredentials = callCredentials;
+    this.channel = channel;
+    this.blockingStub = GreeterGrpc.newBlockingStub(channel);
+  }
+
+  public void shutdown() throws InterruptedException {
+    channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
+  }
+
+  /**
+   * Say hello to server.
+   *
+   * @param name name to set in HelloRequest
+   * @return the message in the HelloReply from the server
+   */
+  public String greet(String name) {
+    logger.info("Will try to greet " + name + " ...");
+    HelloRequest request = HelloRequest.newBuilder().setName(name).build();
+
+    // Use a stub with the given call credentials applied to invoke the RPC.
+    HelloReply response =
+        blockingStub
+            .withCallCredentials(callCredentials)
+            .sayHello(request);
+
+    logger.info("Greeting: " + response.getMessage());
+    return response.getMessage();
+  }
+
+  /**
+   * Greet server. If provided, the first element of {@code args} is the name to use in the greeting
+   * and the second is the client identifier to set in JWT
+   */
+  public static void main(String[] args) throws Exception {
+
+    String host = "localhost";
+    int port = 50051;
+    String user = "world";
+    String clientId = "default-client";
+
+    if (args.length > 0) {
+      host = args[0]; // Use the arg as the server host if provided
+    }
+    if (args.length > 1) {
+      port = Integer.parseInt(args[1]); // Use the second argument as the server port if provided
+    }
+    if (args.length > 2) {
+      user = args[2]; // Use the the third argument as the name to greet if provided
+    }
+    if (args.length > 3) {
+      clientId = args[3]; // Use the fourth argument as the client identifier if provided
+    }
+
+    CallCredentials credentials = new JwtCredential(clientId);
+    AuthClient client = new AuthClient(credentials, host, port);
+
+    try {
+      client.greet(user);
+    } finally {
+      client.shutdown();
+    }
+  }
+}
diff --git a/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/AuthServer.java b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/AuthServer.java
new file mode 100644
index 0000000..90e7dff
--- /dev/null
+++ b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/AuthServer.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2019 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.examples.jwtauth;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.examples.helloworld.GreeterGrpc;
+import io.grpc.examples.helloworld.HelloReply;
+import io.grpc.examples.helloworld.HelloRequest;
+import io.grpc.stub.StreamObserver;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * Server that manages startup/shutdown of a {@code Greeter} server. This also uses a {@link
+ * JwtServerInterceptor} to intercept the JWT token passed
+ */
+public class AuthServer {
+
+  private static final Logger logger = Logger.getLogger(AuthServer.class.getName());
+
+  private Server server;
+  private int port;
+
+  public AuthServer(int port) {
+    this.port = port;
+  }
+
+  private void start() throws IOException {
+    server = ServerBuilder.forPort(port)
+        .addService(new GreeterImpl())
+        .intercept(new JwtServerInterceptor())  // add the JwtServerInterceptor
+        .build()
+        .start();
+    logger.info("Server started, listening on " + port);
+    Runtime.getRuntime().addShutdownHook(new Thread() {
+      @Override
+      public void run() {
+        // Use stderr here since the logger may have been reset by its JVM shutdown hook.
+        System.err.println("*** shutting down gRPC server since JVM is shutting down");
+        AuthServer.this.stop();
+        System.err.println("*** server shut down");
+      }
+    });
+  }
+
+  private void stop() {
+    if (server != null) {
+      server.shutdown();
+    }
+  }
+
+  /**
+   * Await termination on the main thread since the grpc library uses daemon threads.
+   */
+  private void blockUntilShutdown() throws InterruptedException {
+    if (server != null) {
+      server.awaitTermination();
+    }
+  }
+
+  /**
+   * Main launches the server from the command line.
+   */
+  public static void main(String[] args) throws IOException, InterruptedException {
+
+    // The port on which the server should run
+    int port = 50051; // default
+    if (args.length > 0) {
+      port = Integer.parseInt(args[0]);
+    }
+
+    final AuthServer server = new AuthServer(port);
+    server.start();
+    server.blockUntilShutdown();
+  }
+
+  static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
+    @Override
+    public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
+      // get client id added to context by interceptor
+      String clientId = Constant.CLIENT_ID_CONTEXT_KEY.get();
+      logger.info("Processing request from " + clientId);
+      HelloReply reply = HelloReply.newBuilder().setMessage("Hello, " + req.getName()).build();
+      responseObserver.onNext(reply);
+      responseObserver.onCompleted();
+    }
+  }
+}
diff --git a/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/Constant.java b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/Constant.java
new file mode 100644
index 0000000..434422f
--- /dev/null
+++ b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/Constant.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.examples.jwtauth;
+
+import io.grpc.Context;
+import io.grpc.Metadata;
+
+import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
+
+/**
+ * Constants definition
+ */
+final class Constant {
+    static final String JWT_SIGNING_KEY = "L8hHXsaQOUjk5rg7XPGv4eL36anlCrkMz8CJ0i/8E/0=";
+    static final String BEARER_TYPE = "Bearer";
+
+    static final Metadata.Key<String> AUTHORIZATION_METADATA_KEY = Metadata.Key.of("Authorization", ASCII_STRING_MARSHALLER);
+    static final Context.Key<String> CLIENT_ID_CONTEXT_KEY = Context.key("clientId");
+
+    private Constant() {
+        throw new AssertionError();
+    }
+}
diff --git a/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/JwtCredential.java b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/JwtCredential.java
new file mode 100644
index 0000000..4975b4a
--- /dev/null
+++ b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/JwtCredential.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.examples.jwtauth;
+
+import io.grpc.CallCredentials;
+import io.grpc.Metadata;
+import io.grpc.Status;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import java.util.concurrent.Executor;
+
+/**
+ * CallCredentials implementation, which carries the JWT value that will be propagated to the
+ * server in the request metadata with the "Authorization" key and the "Bearer" prefix.
+ */
+public class JwtCredential extends CallCredentials {
+
+  private final String subject;
+
+  JwtCredential(String subject) {
+    this.subject = subject;
+  }
+
+  @Override
+  public void applyRequestMetadata(final RequestInfo requestInfo, final Executor executor,
+      final MetadataApplier metadataApplier) {
+    // Make a JWT compact serialized string.
+    // This example omits setting the expiration, but a real application should do it.
+    final String jwt =
+        Jwts.builder()
+            .setSubject(subject)
+            .signWith(SignatureAlgorithm.HS256, Constant.JWT_SIGNING_KEY)
+            .compact();
+
+    executor.execute(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          Metadata headers = new Metadata();
+          headers.put(Constant.AUTHORIZATION_METADATA_KEY,
+              String.format("%s %s", Constant.BEARER_TYPE, jwt));
+          metadataApplier.apply(headers);
+        } catch (Throwable e) {
+          metadataApplier.fail(Status.UNAUTHENTICATED.withCause(e));
+        }
+      }
+    });
+  }
+
+  @Override
+  public void thisUsesUnstableApi() {
+    // noop
+  }
+}
diff --git a/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/JwtServerInterceptor.java b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/JwtServerInterceptor.java
new file mode 100644
index 0000000..d24e814
--- /dev/null
+++ b/examples/example-jwt-auth/src/main/java/io/grpc/examples/jwtauth/JwtServerInterceptor.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.examples.jwtauth;
+
+import io.grpc.Context;
+import io.grpc.Contexts;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import io.grpc.Status;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.JwtParser;
+import io.jsonwebtoken.Jwts;
+
+/**
+ * This interceptor gets the JWT from the metadata, verifies it and sets the client identifier
+ * obtained from the token into the context. In order not to complicate the example with additional
+ * checks (expiration date, issuer and etc.), it relies only on the signature of the token for
+ * verification.
+ */
+public class JwtServerInterceptor implements ServerInterceptor {
+
+  private JwtParser parser = Jwts.parser().setSigningKey(Constant.JWT_SIGNING_KEY);
+
+  @Override
+  public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall,
+      Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {
+    String value = metadata.get(Constant.AUTHORIZATION_METADATA_KEY);
+
+    Status status = Status.OK;
+    if (value == null) {
+      status = Status.UNAUTHENTICATED.withDescription("Authorization token is missing");
+    } else if (!value.startsWith(Constant.BEARER_TYPE)) {
+      status = Status.UNAUTHENTICATED.withDescription("Unknown authorization type");
+    } else {
+      Jws<Claims> claims = null;
+      // remove authorization type prefix
+      String token = value.substring(Constant.BEARER_TYPE.length()).trim();
+      try {
+        // verify token signature and parse claims
+        claims = parser.parseClaimsJws(token);
+      } catch (JwtException e) {
+        status = Status.UNAUTHENTICATED.withDescription(e.getMessage()).withCause(e);
+      }
+      if (claims != null) {
+        // set client id into current context
+        Context ctx = Context.current()
+            .withValue(Constant.CLIENT_ID_CONTEXT_KEY, claims.getBody().getSubject());
+        return Contexts.interceptCall(ctx, serverCall, metadata, serverCallHandler);
+      }
+    }
+
+    serverCall.close(status, new Metadata());
+    return new ServerCall.Listener<ReqT>() {
+      // noop
+    };
+  }
+
+}
diff --git a/examples/example-jwt-auth/src/main/proto/helloworld.proto b/examples/example-jwt-auth/src/main/proto/helloworld.proto
new file mode 100644
index 0000000..6340c54
--- /dev/null
+++ b/examples/example-jwt-auth/src/main/proto/helloworld.proto
@@ -0,0 +1,37 @@
+// Copyright 2019 The gRPC Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package helloworld;
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}
diff --git a/examples/example-jwt-auth/src/test/java/io/grpc/examples/jwtauth/AuthClientTest.java b/examples/example-jwt-auth/src/test/java/io/grpc/examples/jwtauth/AuthClientTest.java
new file mode 100644
index 0000000..8428009
--- /dev/null
+++ b/examples/example-jwt-auth/src/test/java/io/grpc/examples/jwtauth/AuthClientTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 The gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.grpc.examples.jwtauth;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.delegatesTo;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import io.grpc.CallCredentials;
+import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptors;
+import io.grpc.examples.helloworld.GreeterGrpc;
+import io.grpc.examples.helloworld.HelloReply;
+import io.grpc.examples.helloworld.HelloRequest;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.ServerCall.Listener;
+import io.grpc.ServerInterceptor;
+import io.grpc.stub.StreamObserver;
+import io.grpc.testing.GrpcCleanupRule;
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+
+/**
+ * Unit tests for {@link AuthClient} testing the default and non-default tokens
+ *
+ *
+ */
+@RunWith(JUnit4.class)
+public class AuthClientTest {
+  /**
+   * This rule manages automatic graceful shutdown for the registered servers and channels at the
+   * end of test.
+   */
+  @Rule
+  public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
+
+  private final ServerInterceptor mockServerInterceptor = mock(ServerInterceptor.class, delegatesTo(
+      new ServerInterceptor() {
+        @Override
+        public <ReqT, RespT> Listener<ReqT> interceptCall(
+            ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+          return next.startCall(call, headers);
+        }
+      }));
+
+  private AuthClient client;
+
+
+  @Before
+  public void setUp() throws IOException {
+    // Generate a unique in-process server name.
+    String serverName = InProcessServerBuilder.generateName();
+
+    // Create a server, add service, start, and register for automatic graceful shutdown.
+    grpcCleanup.register(InProcessServerBuilder.forName(serverName).directExecutor()
+        .addService(ServerInterceptors.intercept(
+            new GreeterGrpc.GreeterImplBase() {
+
+              @Override
+              public void sayHello(
+                  HelloRequest request, StreamObserver<HelloReply> responseObserver) {
+                HelloReply reply = HelloReply.newBuilder()
+                    .setMessage("AuthClientTest user=" + request.getName()).build();
+                responseObserver.onNext(reply);
+                responseObserver.onCompleted();
+              }
+            },
+            mockServerInterceptor))
+        .build().start());
+
+    CallCredentials credentials = new JwtCredential("test-client");
+    ManagedChannel channel = InProcessChannelBuilder.forName(serverName).directExecutor().build();
+    client = new AuthClient(credentials, channel);
+  }
+
+  @Test
+  public void greet() {
+    ArgumentCaptor<Metadata> metadataCaptor = ArgumentCaptor.forClass(Metadata.class);
+    String retVal = client.greet("John");
+
+    verify(mockServerInterceptor).interceptCall(
+        ArgumentMatchers.<ServerCall<HelloRequest, HelloReply>>any(),
+        metadataCaptor.capture(),
+        ArgumentMatchers.<ServerCallHandler<HelloRequest, HelloReply>>any());
+
+    String token = metadataCaptor.getValue().get(Constant.AUTHORIZATION_METADATA_KEY);
+    assertNotNull(token);
+    assertTrue(token.startsWith("Bearer"));
+    assertEquals("AuthClientTest user=John", retVal);
+  }
+}