Setting up Signup and Basic Validations

Part II of “Handling Auth in Rails and React” a series

Kenny Marks
Feb 23 · 10 min read

Introduction

Snippets from the previous article

//Routes
Rails.application.routes.draw do
post '/login', to: 'auth#login'
end
//Auth Controller
class AuthController < ApplicationController
def login
user = User.find_by(username: params[:username])
is_authenticated = user.authenticate(params[:password])
if is_authenticated
render json: user
else
render json: {error: “Wrong username/password. Please try again!”}
end
end
end
// Users Model
class User < ApplicationRecord
has_secure_password
end

Step I: Setting up Signup

First we are going to create our users controller in our terminal (If you are continuing directly form part I you can either close your local server or open a new tab, if not be sure to cd into the correct project before entering this command):

rails g controller User

Next we are going to create a new route for sign up:

Rails.application.routes.draw do
post ‘/login’, to: ‘auth#login
post ‘/signup’, to: ‘users#create
end

Step II: Creating the Signup Method

class UsersController < ApplicationController
def create
user = User.create()
end
end

However if we tried to make a user right now we would get back an empty response. The reason for this is that currently we are not permitting our params to be sent back and saved to our API. Remember the params we got back from when we tested our login method:

Image for post
Image for post

Even if we were to continue filling out this method without permitting our params we could not save a new user. To do so we need to create a private method that will create strong parameters allowing a our params to be permitted instead of returning false. At the bottom of the controller we will add:

class UsersController < ApplicationController
def create
user = User.create()
end

private
def user_params
params.permit(:username, :password)
end
# private methods should stay at the bottom of a controller
end

Now that our params are permitted we can now pass our user_params in into our create method. Once we do that our user controller will look like this:

class UsersController < ApplicationController
def create
user = User.create(user_params)
end

private
def user_params
params.permit(:username, :password)
end
end

This will create a user however if we sent a ‘POST’ request it run the request, create the user, but would not render us a user object once it creates the user.

Here, similar to login, we can add “if/else” statement that will check the new users validity. If it comes back true it will render the JSON of the user, if not it will render the errors of why the user is not valid. As of right now the only error we would likely see is if we left a password blank. This is because the has_secure_password validation checks to make sure the password is blank or not.

class UsersController < ApplicationController
def create
user = User.create(user_params)
if user.valid?
render json: user
else
render json: {errors: user.errors.full_messages}
end
end

private
def user_params
params.permit(:username, :password)
end
end

Finally, we can test our signup method in Postman to see that we are creating a new user:

If we change our url to “localhost:3000/signup” and add a new user to our program and hit send we should get back something like this:

Image for post
Image for post
For a refresher on how to set up Postman please refer to the previous article.

Now that we can sign up a user we can add validations to them. To create these validations we will be working in our User model.

Step III: Adding Common Username Validations

In this section we will be going over some common username validations to better secure a users accounts. Here we are going to make sure that when creating a new account a user has passed in a username, that they are unique, and checking that they have a minimum and maximum length (between 6–30 charcters).

The first two validations we are adding are to check if a username is present and that it is unique. There are several ways to do this. First the there is the standard format for a validation for example if we wanted to validate the presence of the username we could write:

validates :username, :presence => true

However rails also provides us with this specific validation:

validates_presence_of

The above example is a bit easier to write so that is the one we will use in this case. So in our user model we will add:

class User < ApplicationRecord
# username validations
validates_presence_of :username
# password validations
has_secure_password
end
// Qucik note: I am sepearting the validations based on username and password to keep them organized

To test that our validation is working we will attempt a signup request with Postman using an empty string, if everything is correct we should get back an error:

Image for post
Image for post

Next we will validate that our usernames are unique following the format as before, but with a few differnces:

class User < ApplicationRecord
# user validations
validates_presence_of :username
validates_uniqueness_of :username, :case_sensitive => false
# password validations
has_secure_password
end

This validation will check that a username is unique regardless of capitalization. For example if someone already chose the name “Testuser” a new user could not use something like “testUser”.

Again we can test through Postman:

Image for post
Image for post

Finally we will validate the length of a username to do so we will check the length of the username and see if it between a range of numbers (6–30):

class User < ApplicationRecord
# username validations
validates_presence_of :username
validates_uniqueness_of :username, :case_sensitive => false
validates :username, :length => { :in => 6..30}
# password validations
has_secure_password
end

And once more we can test it out by changing our username to a single character, or longer than thirty.

Image for post
Image for post
Image for post
Image for post

Now that we have some username validations we will add some password validations.

Step IV: Adding Common Password Validations

Before jumping into writing out our validations I will cover a few of the things has_secure_password gives us. For a complete explanation please refer to the documentation. Has_secure_password gives us access to three separate validations, however we are only going to focus on twp. First that a password is present on the creation of a user, second that it has a maximum number of 72 characters.

In addition to this we will be adding a few extra validations: checking that they certain minimum length (in this case at least 6), and that they have at least one uppercase letter, one lowercase letter, one number, and one special character.

First we will validate its length (since the has_secure_passwords gives already gives us an upper limit we are going to focus on the minimum) :

class User < ApplicationRecord
# username validations
validates_presence_of :username
validates_uniqueness_of :username, :case_sensitive => false
validates :username, :length => { :in => 6..30}
# password validations
has_secure_password
validates :password, :length => { :minimum => 6 }
end

To test we can simply change our password to something less than 6 characters:

Image for post
Image for post

Rather than validate uppercase, lowercase, numbers and symbols individually we will use regex to help us define the format of our password. If you are unfamiliar with what a regular expression is feel free to read up on them here. Put simply, they are a special text string that defines a search pattern.

To start we will create a constant for password complexity called PASSWORD_FORMAT. Most regular expressions will be a single line, but to keep it a little clearer we will separate it into multiple line:

At the top of the our password validations we will create the constant:


class User < ApplicationRecord
# username validations
validates_presence_of :username
validates_uniqueness_of :username, :case_sensitive => false
validates :username, :length => { :in => 6..30}
# password validations
PASSWORD_FORMAT = /\A
(?=.*[A-Z]) # Must contain an uppercase character
(?=.*[a-z]) # Must contain a lowercase character
(?=.*\d) # Must contain a digit
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
has_secure_password
validates :password, :length => { :minimum => 6 }
end

Let’s break down of how this constant works. First it will check that there is at least one capital letter between A-Z. Next it will check that it includes at least one lowercase letter also between a-z. Next it will check and ensure that there is at least one number between 0–9. Finally, the last bit of code will check that there is at least one special character.

Now we will validate our PASSWORD_FORMAT:

class User < ApplicationRecord
# username validations
validates_presence_of :username
validates_uniqueness_of :username, :case_sensitive => false
validates :username, :length => { :in => 6..30}
# password validations
PASSWORD_FORMAT = /\A
(?=.*[A-Z]) # Must contain an uppercase character
(?=.*[a-z]) # Must contain a lowercase character
(?=.*\d) # Must contain a digit
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
has_secure_password
validates :password, :length => { :minimum => 6 }
validates :password, format: { with: PASSWORD_FORMAT, :message => 'Password must include: 1 uppercase, 1 lowercase, 1 digit and 1 special character' }
end

Here we are also adding a message for our validation. What this will do is allow us to render an error(you can add these to your validations if you want to get back a specfic error).When we test it give us the error in our results:

Image for post
Image for post

With this we have created some validations for a username and passwords. Depenidng on the project you may find you need to create more, if so please refer to the official documentation on validations.

Final Code Snippets

//Routes
Rails.application.routes.draw do
post ‘/login’, to: ‘auth#login
post ‘/signup’, to: ‘users#create
end
//Auth Controller
class AuthController < ApplicationController
def login
user = User.find_by(username: params[:username])
is_authenticated = user.authenticate(params[:password])
if is_authenticated
render json: user
else
render json: {error: “Wrong username/password. Please try again!”}
end
end
end
// Users Controller
class UsersController < ApplicationController
def create
user = User.create(user_params)
if user.valid?
render json: user
else
render json: {errors: user.errors.full_messages}
end
end

private
def user_params
params.permit(:username, :password)
end
end
// Users Model
class User < ApplicationRecord
# username validations
validates_presence_of :username
validates_uniqueness_of :username, :case_sensitive => false
validates :username, :length => { :in => 6..30}
# password validations
PASSWORD_FORMAT = /\A
(?=.*[A-Z]) # Must contain an uppercase character
(?=.*[a-z]) # Must contain a lowercase character
(?=.*\d) # Must contain a digit
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
has_secure_password
validates :password, :length => { :minimum => 6 }
validates :password, format: { with: PASSWORD_FORMAT, :message => 'Password must include: 1 uppercase, 1 lowercase, 1 digit and 1 special character' }
end

Conclusion

In this article we covered creating a signup method for new users. Once we were able to sign up new users we added several validations to prevent duplicate accounts, as well as making sure our users are creating strong passwords. The next article in this series will cover persisting a user using JWT. Stay Tuned!

The Startup

Medium's largest active publication, followed by +684K people. Follow to join our community.

Sign up for Top Stories

By The Startup

A newsletter that delivers The Startup's most popular stories to your inbox once a month. Take a look

Create a free Medium account to get Top Stories in your inbox.

Kenny Marks

Written by

A full stack developer with an interest in Cybersecurity, International Relations, and Gaming.

The Startup

Medium's largest active publication, followed by +684K people. Follow to join our community.

Kenny Marks

Written by

A full stack developer with an interest in Cybersecurity, International Relations, and Gaming.

The Startup

Medium's largest active publication, followed by +684K people. Follow to join our community.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store