Secure Communication with Token-based RSocket
Secure Communication with Token-based RSocket
Take a look at how you can establish secure communication with token-based RSocket, and establish a clear understanding of JWT.
Join the DZone community and get the full member experience.Join For Free
RSocket provides a message-driven communication mechanism, by using the reactive streaming framework, and supports most of the protocols (TCP/WebSocket/HTTP 1.1&HTTP 2). Furthermore, it’s program language-agnostic interaction models (REQUEST_RESPONSE/REQUEST_FNF/REQUEST_STREAM/REQUEST_CHANNEL) cover most communication scenarios, from the Microservices, API Gateway, and Sidecar Proxy, to the Message Queue.
Considering security for the communication, it's easy to use TLS-based and Token-based solution in RSocket-based productions. RSocket can reuse the TLS over the TCP or WebSocket directly, but to demonstrate the RBAC feature vividly, in this article, we only talk about the token-based implementation.
As you know, JSON Web Token (JWT) is the most popular technology for OAuth2 in the world. And it’s also program language-agnostic. After some investigations, I believe RSocket with JWT is a great way to implement secure communication between the services, especially for Open API. Now, let’s take a deeper look into what happens.
The first question is how to use the token to talk between the services in RSocket.
There are two ways to send a token from the requester to the responder. We can put the token into metadata API at the setup time, and the other way is to take the token as metadata, along with payload as data, in every request time.
Beyond that, the routing plays the crucial role for authorization, which indicates the resource on responder side. In RSocket extensions, there is a routing metadata extension to extend the four interaction models. If the tag payloads are supported in both requester and responder, beneath, then it's easy to define the authorization on the top layer.
In brief, to understand this article, if you know five things below about JWT, that's enough.
HS256 is a secret-based algorithm, and RS256/ES256 is a PKI-based one. All of them are defined in the JWA spec. HS256 is HMAC (Keyed-Hash Message Authentication Code) + SHA-256, RS256 is RSASSA + SHA-256, and RS256 is ECDSA(Elliptic Curve Digital Signature Algorithm) + SHA-256.
In regard to the secret length, assuming that we use HS256 as the token algorithm, the secret characters should be more than 32, because in HS256, the secret should be at least 256 bits (
1 character = 8 bits).
Access Token is used by the responder to decode/verify and authorize, and Refresh Token is used to regenerate the tokens, especially when the access token is expired.
It must be handled after the user signs out, and the access token is still valid during the period.
It’s time to show the demo. We have two kinds of API, token and resource. Only when the token is verified, the resource API could be accessed.
- We use
signinAPI to generate tokens to requester, and it takes username and password. After authenticate, the responder will sign, save and return the Access Token and Refresh Token to the requester.
refreshapi is to renew the tokens, and it takes refresh token. After decode and authorize, the responder will sign, save and return the Access Token and Refresh Token to the requester.
fireas the resource API to demonstrate different read/write actions.
signoutAPI is to handle the stolen case, as we talked above.
Since we use Role-Based Access Control (RBAC) as the authorization mechanism, in the authentication part, we should provide an identity (User-Role-Permission) repository to save and retrieve the identity information in the responder.
Besides, we provide a token repository to store/revoke/read the tokens, which is used to verify the authentication decoded from the token. Since the authorization information is encrypted and compressed in the token, we use the information from the repository to double-check these two authorizations. If they are the same, we can say that the request is authentic.
Spring Boot Implementation
As my plan is to use multiple program languages to show this demo, we must ensure the algorithm and some constants, to unify the way to encrypt and compress.
In this demo, we use HS256 as the algorithm, and define the access token expired time as
5 minutes, refresh token expire time as
I will show you the token generated code:
A Word of Caution:
The claim key name in the above code is not arbitrary, since “scope” is used in framework as the default way to decode the role from the token.
Accordingly, the token decoder code is here:
The decode method in
HelloJwtDecoder will be invoked by the framework in every request handling time, to convert the token string value to jwt:
To simplify the environment of the demo running, the way to revoke token implements here by guava cache. You can use some powerful components, like Redis, to do that.
Once the time is up, the access token will be revoked automatically.
On the other hand, when the requester sends
signout, this cache will be invoked as event-driven.
authenticate function for
signin, is as simple as the HTTP basic authentication did:
By contrast, the other
authenticate, which is for
refresh, is a little more complex, the steps are:
Getting decoder and using it to decode the token string value to a JWT object
Using the reactive style to map JWT to auth
Retrieving the auth from repository
Verifying the auths from db and token are same
Returning the auth object in streaming way
As I said, this demo is based on RBAC; the routing is the key. And for brevity, no more for showing the open APIs version, just a little:
I put the route-based RBAC defination in parent class to easy to extend the security by using other way, e.g. TLS.
MessageMapping annotation to let us define the route for messaging, which means streaming api in RSocket.
2.2.0-Release , Spring Boot start to support RSocket. And from 2.3, it supports RSocket security. Since 2.3.0 is not GA when I write this article, the version I show you is
Build, Run, and Test
The resource API part shows the hiring and firing employee. Please read more details from Eighteen_Arhats!
In the End
I was going to show a golang version, but so far, the RSocket for golang is not an open routing API and it’s not convenient to achieve that. But, there’s a good news that Jeff will open them, soon.
It’s funny for me to show this demo by using other languages, Rust/NodeJs and so on. Maybe, I would go on to write a series of articles.
By the way, the source code for this demo is on GitHub.
Opinions expressed by DZone contributors are their own.