introduce

This article describes how to implement server-side JWT authentication logic through RK-boot.

What is JWT?

JSON network tokens are an Internet standard for creating data with optional signatures or optional encryption that allows declarations to be securely expressed between two parties. Tokens are signed using a private secret or public/private key.

To put it simply, the JWT mechanism enables the client to encrypt the information through a key, add it to the Header of the HTTP request, and send it to the server to verify the validity of the client. Please refer to the JWT website

Many SAAS services, such as Github, use JWT for user authentication

Please visit the following address for the full tutorial:

  • rkdocs.netlify.app/cn

The installation

go get github.com/rookie-ninja/rk-boot
go get github.com/rookie-ninja/rk-grpc
Copy the code

Quick start

Rk-boot enables grPC-gateway for the gRPC service by default. The two protocols listen to the same port.

So JWT can be validated for both gRPC and Restful requests.

1. Create the boot. Yaml

The boot.yaml file tells RK-boot how to start the gRPC service.

In the YAML file below, we declare three things:

  • Enable commonService: Apis like /rk/v1/healthy are provided by default. details
  • Enable enableRkGwOption: This Option specifies gRPC -> Restful API error types. Otherwise, gRPC error codes will be returned. Recommended.
  • As an example, turn on the JWT interceptor and declare the JWT key as my-secret. The HS256 algorithm will be used in the background by default, and you can also customize the algorithm, which will be described below.

So boot.yaml tells RK-boot to turn on port 8080 to start gRPC service and to turn on commonService and JWT validation interceptor.

---
grpc:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    enableRkGwOption: true            # Optional
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      jwt:
        enabled: true                 # Optional, default: false
        signingKey: "my-secret"       # Required
Copy the code

2. Create a main. Go

Since we have commonService enabled, there is no need to add an additional API to gRPC for validation.

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package rkdemo

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
        _ "github.com/rookie-ninja/rk-grpc/boot"
)

// Application entrance.
func main(a) {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}
Copy the code

3. Create a JWT Token

Depending on the language, there are many open source libraries that can help you create JWT tokens. Please refer to the official website

This Token uses my-Secret as the key and HS256 as the algorithm, the same configuration as boot.yaml.

4. Verify

  • Send a request to/RK /v1/ HEALTHY and provide the legitimate JWT Token created above.
$ curl localhost:8080/rk/v1/healthy -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.EpM5XBzT JZ4J8AfoJEcJrjth8pfH28LWdjLo90sYb9g" {"healthy":true}Copy the code
  • Send a request to/RK /v1/ HEALTHY to provide illegal JWT
$ curl localhost:8080/rk/v1/healthy -H "Authorization: Bearer invalid-jwt-token"
{
    "error":{
        "code":401,
        "status":"Unauthorized",
        "message":"invalid or expired jwt",
        "details":[
            {
                "code":16,
                "status":"Unauthenticated",
                "message":"[from-grpc] invalid or expired jwt"
            },
            {
                "code":2,
                "status":"Unknown",
                "message":"token contains an invalid number of segments"
            }
        ]
    }
}
Copy the code

JWT interceptor option

Rk-boot provides several JWT interceptor options, and override options are not recommended except for special needs.

options describe type The default value
grpc.interceptors.jwt.enabled Start the JWT interceptor boolean false
grpc.interceptors.jwt.signingKey B: Yes, it’s important to know string “”
grpc.interceptors.jwt.ignorePrefix JWT for a particular gRPC method is not validated string “”
grpc.interceptors.jwt.signingKeys Multiple singing keys are provided. Please refer to the following description []string []
grpc.interceptors.jwt.signingAlgo Rk-boot uses Golang-jwt/JWT as the default signature algorithm. For details about the supported algorithm types, see the following string HS265
grpc.interceptors.jwt.tokenLookup The method for interceptors to find CSRF tokens is described below string “The header: Authorization”
grpc.interceptors.jwt.authScheme Auth Scheme, also known as the Scheme that follows the Authorization header string Bearer

Supported signature algorithms

HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, EdDSA

TokenLookup format

GRPC can only read requests from headers. If gRPC requests are sent by gRPC, headers are grPC. metadata.

// Optional. Default value "header:Authorization".
// Possible values:
// - "header:<name>"
// Multiply sources example:
// - "header: Authorization"
Copy the code

A variety of signing keys

Please refer to the RFC7515

The signing Key is used to separate Key and Value.

signingKeys:
  - "key:value"
Copy the code

GRPC protocol JWT example

In the previous example, we used Restful apis for the request example. This time, we use gRPC.

Use grpcurl to call the gRPC service directly.

To enable grpcurl, we need to enable GRPC Reflection in boot.yaml by enableReflection.

  • boot.yaml
---
grpc:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    enableRkGwOption: true            # Optional
    enableReflection: true            # Optional
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      jwt:
        enabled: true                 # Optional, default: false
        signingKey: "my-secret"       # Required
Copy the code
  • Send the request to the rk API. V1. RkCommonService. Healthy, and provide legal JWT Token.
$ grpcurl -plaintext -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.EpM5XBzT JZ4J8AfoJEcJrjth8pfH28LWdjLo90sYb9g" localhost:8080 rk.api.v1.RkCommonService.Healthy { "healthy": true }Copy the code
  • Send a request to/RK /v1/ HEALTHY to provide illegal JWT
$ grpcurl -plaintext -H "Authorization:Bearer invalid-jwt-token" localhost:8080 rk.api.v1.RkCommonService.Healthy
Error invoking method "rk.api.v1.RkCommonService.Healthy": rpc error: code = Unauthenticated desc = failed to query for service descriptor "rk.api.v1.RkCommonService": invalid or expired jwt
Copy the code