Create a Proto File

First, define message payloads and RPC operations for the authentication service. We’ll have two RPC operations:

  • Authenticate
    Input:username, password
    Output:Check the username/password pair against the entry in the user repository. If it matches, return a signed JWT token.|
  • Authorization
    Input:token
    Output:If the token is valid, retrieve the user, and return a list of associated roles. If not, user was not found, return Status.NOT_FOUND error. If JWT was not verifiable, return Status.UNAUTHENTICATED error.

1 - Initialize the Proto file

Create / open the proto file: auth-service/src/main/proto/AuthService.proto

  • See that the Protobuffer syntax to proto3 to ensure we are using Protobuffer 3 syntax.
  • Specify the package. The package corresponds to the Java packages.
  • Set protoc options with option keyword. By default, the generator will generate all of the message classes into a single giant class file. In this example, we’ll generate each of the Protobuffer message into its own class by setting the java_multiple_files option.
// 1. Set the syntax, package, and options
syntax = "proto3";
option java_multiple_files = true;
package com.example.auth;

2 - Define messages

After defining the syntax, package, and options, define the message payloads. Notice that every field is strongly typed. There is a variety of out of the box primitives:

  • Primitives such as string, int32, int64, bool, etc.
  • To represent an array, use repeated, e.g., repeated string
  • Strongly typed map/hash, e.g., map<string, int64>
  • Custom types, such as
    • Nested types
    • Re-use defined types
    • Enumerations, e.g. enum Sentiment { HAPPY = 0; SAD = 1; }

Every field must be assigned a tag number (e.g., string username = 1). The tag number is what uniquely identifies the field rather than the name of the field. When serialized, the tag number is stored in the serialized payload and transferred across the wire.

syntax = "proto3";
...

// 2. Add the message definitions
message AuthenticationRequest {
    string username = 1;
    string password = 2;
}

message AuthenticationResponse {
    string token = 1;
}

message AuthorizationRequest {
    string token = 1;
}

message AuthorizationResponse {
    string username = 1;
    repeated string roles = 2;
}

3 - Define service and operations

After defining the message payload, define the service, and operations within the service. Every operation must have an input type and an output type.

For the Authentication service, we’ll define an unary request, which is basically a simple request/response:

syntax = "proto3";
...

message AuthenticationRequest {
    ...
}
...
// 3. Add the service definitions
service AuthenticationService {
    rpc authenticate(AuthenticationRequest) returns (AuthenticationResponse);
    rpc authorization(AuthorizationRequest) returns (AuthorizationResponse);
}