Rails basic API authentication with Sorcery and JWT

APIs have become more and more popular due to increasing demand for development of mobile and single page apps, and so the need of sessionless server-side applications, being those that are token-based the most popular ones now days due to their relative easy implementation. Here you have a great example that uses JWT to secure a microservice-based application.

JWT (or jot) stands for JSON Web Token and as it states it is a web token structured in a JSON format. JWTs are composed of 3 parts: the header, the payload, and the signature. In short, the header contains the type of token and the algorithm for the cryptographic signing; the payload (or claim) contains the information wanted to securely send to the server, and the signature which contains the secret key known only by the server and that is used to decrypt the data contained in the payload. It is not the primary objective of this post to explain in detail what JWT is, so if you want to know more about it you can do it here.

JWTs are used in the next way:

The API server generates the JWT.

The JWT is sent to the client.

The client saves the JWT (i.e. in LocalStorage).

The client sends the JWT back –we’ll send it on a request’s header– to the server.

The server uses the JWT’s payload data for X or Y purpose. In our case to identify the user that is performing the request.

Besides JWT, we need an authentication strategy to actually identify users. We’ll use Sorcery for this purpose; I recommend you to go ahead and read the documentation if you haven’t done so. You can implement any other authentication library –or build your own– but for this particular case I personally like Sorcery because it offers all basic authentication features and it is not as overkilling as other libraries.

This is how the general authentication flow looks like:

Fig. 1 JWT Flow

Conveniently there is a jwt library that we can use to generate user’s token, so go ahead, read its documentation and install it in your project.

Next, we’ll create a service to provide the JWT, another one to validate it, and a last one to authenticate the user.

app/services/jwt/token_provider.rb

Ruby

1

2

3

4

5

6

7

8

9

10

11

12

moduleJwt::TokenProvider

extendself

defcall(payload)

issue_token(payload)

end

private

defissue_token(payload)

JWT.encode(payload,Rails.application.secrets.secret_key_base)

end

end

app/services/jwt/token_decriptor.rb

Ruby

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

moduleJwt::TokenDecryptor

extendself

defcall(token)

decrypt(token)

end

private

defdecrypt(token)

begin

JWT.decode(token,Rails.application.secrets.secret_key_base)

rescue

raiseInvalidTokenError

end

end

end

classInvalidTokenError<StandardError;end;

app/services/jwt/user_authenticator.rb

Ruby

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

moduleJwt::UserAuthenticator

extendself

defcall(request_headers)

@request_headers=request_headers

begin

payload,header=Jwt::TokenDecryptor.(token)

returnUser.find(payload['user_id'])

rescue=>e

# log error here

returnnil

end

end

deftoken

@request_headers['Authorization'].split(' ').last

end

end

NOTE: In this case we are assuming that JWT is in ‘Authorization’ header with format ‘Bearer xxxx.yyyy.zzzz’, that’s why we are splitting @request_headers['Authorization'].

Implementing our services

We need to generate the JWT after user has successfully logged in:

Ruby

1

2

3

4

5

6

7

8

9

10

11

12

classSessionController<ApplicationController

defcreate

user=User.authenticate(params[:email],params[:password])# This method comes from Sorcery

ifuser

token=Jwt::TokenProvider.(user_id:user.id)

renderjson:{user:user,token:token}# As an extra security metric, you might want to make sure you don’t send user’s encrypted password in `user: user` ;)

else

renderjson:{error:'Error description'},status:422

end

end

end

Or signed up:

Ruby

1

2

3

4

5

6

7

8

9

10

11

12

13

classUsersController<ApplicationController

defcreate

user=User.new(user_params)

ifuser.save

token=Jwt::TokenProvider.(user_id:user.id)

renderjson:{user:user,token:token}

else

renderjson:{error:'Error description'},status:422

end

end

end

The token returned in the response should be properly saved in the client so it is sent back to the server in subsequent requests’ headers.

Then we need to authenticate user on future requests. Adding an authentication method on ApplicationController will let us use it on ApplicationController’s children controllers to protect our private endpoints:

Ruby

1

2

3

4

5

6

7

8

9

10

11

12

13

classApplicationController<ActionController::Base

defauthenticate

renderjson:{errors:'Unauthorized'},status:401unlesscurrent_user

end

defcurrent_user

@current_user||=Jwt::UserAuthenticator.(request.headers)

end

end

classAnotherController<ApplicationController

before_filter:authenticate

end

And that’s it, our API is now secured using a sessionless approach. From this point on you can add more complexity using jwt and Sorcery‘s methods to, for instance, make token expire after 1 hour or reset user’s password.

Let me know in the comments section below what you think about this strategy.