Implement Server Stub ===================== Open `auth-service/src/main/java/com/example/auth/grpc/AuthServiceImpl.java`. In this file, we need to: 1. Extend from the gRPC generated AuthServiceBaseImpl class 2. Implement the authenticate and authorization methods Extend the Base Implementation ------------------------------ .. code-block:: console ... // TODO Extend gRPC's AuthenticationServiceBaseImpl public class AuthServiceImpl extends AuthenticationServiceGrpc.AuthenticationServiceImplBase { ... } Override and Implement the Authenticate and Authorize Methods ------------------------------------------------------------- The base implementation bootstraps the underlying descriptors and pipes necessary for gRPC server to communicate with the actual service implementation, and makes the call to the actual service methods. The service methods have default implementations in the base class, but they will all throw Status.UNIMPLEMENTED error. You’ll need to override these methods explicitly: Override Authenticate --------------------- Override the authenticate() base implementation method .. code-block:: console // TODO Override authenticate method @Override public void authenticate(AuthenticationRequest request, StreamObserver responseObserver) { ... } Notice that even though we defined a simple unary call (request/response), the server implementation is fully asynchronous. That means, to return the value or errors to the client, you must use responseObserver. 1. Use `UserRepository` to retrieve the user based on the username. .. code-block:: console User user = repository.findUser(request.getUsername()); 2. If user doesn’t exist, return `Status.UNAUTHENTICATED` error 3. `responseObserver.onError(...)` will close the stream. You should return and avoid calling any other `responseObserver` callbacks afterwards. 4. Similarly, if user exists, but the password doesn’t match, also return `Status.UNAUTHENTICATED` error using `responseObserver.onError(...)` .. code-block:: console if (user == null || !user.getPassword().equals(request.getPassword())) { responseObserver.onError(Status.UNAUTHENTICATED.asRuntimeException()); return; } 5. Finally, if all things goes well, generate a JWT token and return it to the it. You must call `responseObserver.onCompleted()` to completed the call. Otherwise, the client will wait until that is called. If you never call it - then the client will wait forever. .. code-block:: console String token = generateToken(request.getUsername()); responseObserver.onNext(AuthenticationResponse.newBuilder() .setToken(token) .build()); responseObserver.onCompleted(); Override Authorize ------------------ Override the authorize() method: .. code-block:: console // TODO Override authorization method public void authorization(AuthorizationRequest request, StreamObserver responseObserver) { ... } Use `jwtFromToken(...)` to verify the token .. code-block:: console DecodedJWT jwt = jwtFromToken(request.getToken()); Catch JWTVerificationException: .. code-block:: console try { DecodedJWT jwt = jwtFromToken(request.getToken()); } catch (JWTVerificationException e) { responseObserver.onError(Status.UNAUTHENTICATED.asRuntimeException()); return; } If JWT token is valid, a DecodedJWT object will be returned. From there, extract the username using getSubject() .. code-block:: console String username = jwt.getSubject(); User user = repository.findUser(username); if (user == null) { // send error return; } Use `UserRepository` to retrieve the user, construct `AuthorizationResponse` with the roles, then return that. .. code-block:: console responseObserver.onNext(AuthorizationResponse.newBuilder() .addAllRoles(user.getRoles()) .build()); responseObserver.onCompleted(); `Hint: Full implementation here `_