4

Server-side authentication using GraphQL + JWT + Ruby on Rails

 3 years ago
source link: https://medium.flatstack.com/server-side-authentication-using-graphql-jwt-ruby-on-rails-8820e0471ed4
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Image for post
Image for post

I want to share with you how I implemented a simple authentication system using GraphQL and JWT in my Ruby on Rails app. This article is intended for those who are already familiar with technologies such as GraphQL and JWT. For beginners, I’ll attach links at the end of the article.

Pre-requirements:

Connect GraphQL to app

By documentation you just should run rails g graphql:install that will mount our app with GraphQL: it generates GraphQL endpoint, adds app/graphql folder with some base objects.

Execution error responder

To build an error message if some action will fail I added concern to app/graphql/concerns folder that uses GraphQL::ExecutionError class from graphql-ruby gem to raising error messages. This class will add the “errors” key to the response that contains the error message, status, code, and location. I included ExecutionErrorResponder into BaseMutation object to use execution_error method in mutations.

Image for post
Image for post
1st pic. app/graphql/concerns/execution_error_responder.rb

Below I attached an example of a response with error message:

Image for post
Image for post
2nd pic.

User registration mutation

To add the ability to register user, I created Mutations::RegisterUser mutation that takes email, password, and password_confirmation as arguments that looks like as:

Image for post
Image for post
3rd pic. app/graphql/mutations/register_user.rb

Method #resolve takes all available attributes that are described in 3–5 lines (3rd pic.) and then we can modify or sanitize these params if needed. Since mutation returns values via fields, we should describe them in this class like in 7–8 lines (3rd pic.). null: false in this case, means that the field always contains the value.

But I wanted to refactor this code a little bit and moved field describing in a separate folder named “payloads”:

Image for post
Image for post
4th pic. app/graphql/mutations/register_user.rb
Image for post
Image for post
5th pic. app/graphql/types/payloads/register_user_type.rb

For implementing registration logic I used organizer from interactor gem (Users::Register.call(user_params: params) on line 18 in the 4th pic.) that firstly saves new user in the database and then generates JWT for him. I did not use refresh tokens in my case, I am just generating a token that is valid for one day. I will talk about the implementation of the token generation a little later.

To check locally how works registration system I use helpful GUI for editing and testing GraphQL queries and mutations is https://www.electronjs.org/apps/graphiql.

Image for post
Image for post
6th pic.

And we receive the next response for the query above:

Image for post
Image for post
7th pic.

If registerUser mutation will be failed, I will receive an error message that generates by ExecutionErrorResponder in response:

Image for post
Image for post
8th pic.

User login mutation

To log in user, I use almost similar logic to registerUser mutation. But for this case, I permit only required email and password as arguments:

Image for post
Image for post
9th pic. app/graphql/mutations/login_user.rb

In the organizer (line 17 on 9th pic.), I first try to find existed user and then generate a token for him. Payload data for this mutation are identical to registerUser:

Image for post
Image for post
10th pic. app/graphql/types/payloads/login_user_type.rb

Authentication implementation

I will not go into detail about how the JWT works, as there are a lot of such articles on the Internet. Here I just wanted to show how use GraphQL with JWT technology. As I wrote earlier, I generate a token for each case when a user logs in or signs up. I use common interactor that responsible for this action. You can also use another algorithm for cryptographic signing or add some payload data.

Image for post
Image for post
11th pic. app/interactors/users/generate_token.rb

When we have generated token for the user we should authenticate him for each request. To realize this action I wrote some decoder that receives user ID from the token that will be placed in the request header. I wrapped this logic into controller’ concerns:

Image for post
Image for post
12th pic. app/controllers/concerns/authenticable_user.rb

GraphQL provides the ability to pass some specific values for the request using context. In my case, to share current_user with GraphQL mutations I used this feature. I just pass current_user in GraphqlController like as:

Image for post
Image for post
13th pic. app/controllers/graphql_controller.rb

But that is not all. We also should implement authentication logic on the GraphQL side. I used #ready? method for this that was provided by gem. It allows checking current_user existence before running mutations. I also wrapped this logic into concerns to simply include in each mutation:

Image for post
Image for post
14th pic. app/graphql/concerns/authenticable_api_user.rb

The method #ready? will be run before #resolve and if current_user will be nil method will raise an unauthorized error. For example, I tried to create a company as an unauthorized user:

Image for post
Image for post
15th pic.

And then an error message:

Image for post
Image for post
16th pic.

That’s all it takes to easily implement an authentication system using JWT and GraphQL in Ruby on Rails apps. I will be happy to answer your questions.

Useful links:

https://en.wikipedia.org/wiki/JSON_Web_Token

https://graphql-ruby.org/

https://github.com/jwt/ruby-jwt

https://www.electronjs.org/apps/graphiql

https://jwt.io/introduction/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK