DEV Community

Cover image for How to Build an Auth API in Rust gRPC
Neeraj Sharma
Neeraj Sharma

Posted on

How to Build an Auth API in Rust gRPC

Introduction

In this post, I'll explain how to develop an authentication API in Rust using the gRPC protocol. The authentication will
be based on JWT tokens. We'll also create a docker file to deploy the application in a docker container. Lastly, I'll
provide instructions on how to deploy the app.

Features

  • User SignUp
  • User login
  • Password reset
  • OTP verification
  • Password Hashing

Project setup

Create a new project and open it in vs code editor

cargo new auth_api && cd auth_api && code . 
Enter fullscreen mode Exit fullscreen mode

Add the following dependencies to the Cargo.toml file:

[package]
name = "auth_api"
version = "0.1.0"
edition = "2021"

[dependencies]

tokio = { version = "1.0", features = ["full"] }
tonic = "0.12.2"
tonic-reflection = "0.12.2"
prost = "0.13.2"
prost-types = "0.13.2"
jsonwebtoken = "9.3.0"
chrono = { version = "0.4.38", features = ["serde"] }

[build-dependencies]
tonic-build = "0.12.2"

Enter fullscreen mode Exit fullscreen mode

Permission claims

I will use JSON Web Tokens for authentication, which will include custom claims to assign granted permissions to a
user. The perms and req_perms claims will contain permission strings like "CreateAccount", "ApiAccess", "
ResetPassword", and "DeleteAccount".

SignUp account feature

In this section, I'll explain how to implement the signup feature.

A new InitiateSignUp endpoint is required to begin the signup process. This endpoint will generate an OTP code and a
temporary token. The user will receive the temporary token in the response and an OTP code via email. The user must then
submit the OTP code to verify their email address. Upon confirmation of the email address, the user will be permitted to
create a new account.

The following describes the flow of the signup process:

  1. The user initiates a request to sign up for a new account. The server generates an OTP code and sends this to
    user's email then the server responds with a temporary token.
    This token includes the req_perms claim, detailing the permissions the user seeks, in this case, the permission to

    create a new account ie CreateAccount.

  2. In the subsequent request, the user submits the valid OTP together with the temporary token to confirm their email
    address. Upon receiving the OTP code and the temporary token, the server validates the OTP. If it is correct, the
    server creates a new temporary token with a perms claim and moves all permissions from the req_perms to
    the perms claim. The server then sends a response with this new temporary token.

  3. Finally, the user can create a new account by sending their details along with the temporary token.

Proto definition

Now let's create the required rpc methods for creating account feature.
create a new file auth.proto in the proto directory and add the following code:

// `/proto/auth.proto`

syntax = "proto3";
package tutorial.api.auth;

service AuthService {
    rpc InitiateSignUp(InitiateSignUpRequest) returns (InitiateSignUpResponse);
    rpc CreateAccount(CreateAccountRequest) returns (CreateAccountResponse);
    rpc VerifyOtp(VerifyOtpRequest) returns (VerifyOtpResponse);
}

message InitiateSignUpRequest {
    string email = 1;
}

message InitiateSignUpResponse {
    bool success = 1;
    string message = 2;
    string token = 3;
}

message CreateAccountRequest {
    string email = 1;
    string password = 2;
    string first_name = 3;
    string last_name = 4;
}

message CreateAccountResponse {
    bool success = 1;
    string message = 2;
    string token = 3;
    User user = 4;
}

message VerifyOtpRequest {
    string token = 1;
    string otp = 2;
}

message VerifyOtpResponse {
    bool success = 1;
    string message = 2;
    string token = 3;
}

message User {
    string username = 1;
    string email = 2;
    string full_name = 3;
    Gender gender = 4;
    google.protobuf.Timestamp birth_date = 5;
    google.protobuf.Timestamp created_at = 6;

    enum Gender { 
        MALE = 0;
        FEMALE = 1;
        OTHER = 2;
    }
}
Enter fullscreen mode Exit fullscreen mode

Compiling the proto file

Create a build.rs file in root of the project and add the following code:

// `/build.rs`

use std::{env, error::Error, path::PathBuf};

fn main() -> Result<(), Box<dyn Error>> {
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

    // define the service descriptor path for the service reflection
    let service_descriptor = out_dir.join("service_descriptor.bin");

    tonic_build::configure()
        // this is necessary for the grpc reflection 
        .file_descriptor_set_path(service_descriptor)
        // compile the auth.proto
        .compile(&["proto/auth.proto"], &["proto"])?;

    Ok(())
}
Enter fullscreen mode Exit fullscreen mode

Service Implementation

Write a nested module according to the package name in the auth.proto file.

mod tutorial {
    pub mod api {
        pub mod auth {
            // include the compiled code by package name
            tonic::include_proto!("tutorial.api.auth");
            // include the service descriptor set for the reflection service
            pub(crate) const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("service_descriptor");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now add the following code the main function


use std::error::Error;
use tonic::transport::Server;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let addr = "0.0.0.0:50051".parse().unwrap();
    println!("Running server on {addr}");

    let reflection = tonic_reflection::server::Builder::configure()
        .register_encoded_file_descriptor_set(tutorial::api::auth::FILE_DESCRIPTOR_SET)
        .build_v1alpha()
        .unwrap();

    Server::builder()
        .add_service(reflection)
        .serve(addr)
        .await?;

    Ok(())
}

Enter fullscreen mode Exit fullscreen mode

Now if you build and run the application and test the server using Postman It will show you available services by reflection. You don't need to provide any proto file. But for now, It does not implement the proto services.

I will discuss this topic in a future post 🔮. For now, please share your interests.

Top comments (0)