Angular1.X user forgot password flow using REST API

19 February 2015


Forgot password flow using angularJS and Java

People usually have a lot of different passwords for a lot of different websites and online services, forgetting a password is a very common thing and almost every website that holds user account data has a forgot password mechanism flow to allow users to either generate a new password or type in a new password based on their email or another personal piece of information. A typical example:

Login Page

TEXT screenshot

Reset pasword page

TEXT screenshot

On this post I will demonstrate a sample forgot password flow, where a user will be able to generate a new password (this post is not intended to target a full complete scenario therefore it doesn't deal with some other aspects like security and messaging). For sake of brevity I'm ommiting some implementation details but which one can potentially deduce or conclude based from evidence on the example.


Runtime Environment used on example

the runtime environment used on the example is based on the following stack running on Ubuntu Linux 15.04 (vivid):

Frontend:

  • AngularJS (1.3.12 or later)
  • NGINX (1.6.2 or later).

---------------------------- JSON --------------------------------------------------------

Backend:

  • javaEE EAR (javaEE 6 or later).
  • Glassfish (4.0 or later).

---------------------------- SQL ---------------------------------------------------------

PostgreSQL: (9.4.0 or later)

OS: Linux Ubuntu (15.04)


User model is based on this domain model.

Other good practices.




Presentation

on the html a form or div will accomodate user credentials for reseting the password basically containing the user email like in the Reset password FORM example:

HTML:

            <div>
              <form id="resetPasswordForm" novalidate="novalidate">
                <div ng-controller="ResetPasswordCtrl">
                    <div>
                        <h3>forgot Your Password?</h3>
                    </div>
                    <div>
                        Please insert your email:
                    </div>                  
<di> <label>email</label> <input type="text" id="email" name="email" ng-model="resetPasswordEmail" /> </div> <div> <button ng-click="resetPassword()">Reset</button> </div> <div> <a href="#/login">back</a> </div>
</div> </form> </div>

once user types the email and clicks on Reset button, an Angular controller will handle the ng-click event sending an AJAX request to the server-side.


Frontend

Angular JS service:

          
services.factory('UserService', ['$resource', function($resource){
return $resource('/myapp/rest/user/:id', { id: "@id" }, {
'resetPassword': {method: 'GET', params: {email: '@email'}, url: '/myapp/rest/user/reset-password/:email'} } ); }]);

Angular JS controller:

          
controllers.controller('ResetPasswordCtrl', ['$scope', '$location', 'UserService', function ResetPasswordCtrl($scope, $location, UserService) {

    <span class="c1">// action handler for ng-click on form</span>
        <span class="nx">$scope</span><span class="p">.</span><span class="nx">resetPassword</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
            <span class="nx">UserService</span><span class="p">.</span><span class="nx">resetPassword</span><span class="p">({</span><span class="na">email</span><span class="p">:</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">resetPasswordEmail</span><span class="p">},</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">successHandlerResetPassword</span><span class="p">,</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">errorHandler</span><span class="p">);</span>
        <span class="p">};</span>

        <span class="nx">$scope</span><span class="p">.</span><span class="nx">errorHandler</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
              <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
              <span class="nx">$location</span><span class="p">.</span><span class="nx">path</span><span class="p">(</span><span class="s1">'/error'</span><span class="p">);</span>
        <span class="p">};</span>

    <span class="nx">$scope</span><span class="p">.</span><span class="nx">successHandlerResetPassword</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">httpResponse</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'SUCCESS!'</span><span class="p">);</span>
          <span class="nx">$location</span><span class="p">.</span><span class="nx">path</span><span class="p">(</span><span class="s1">'/home'</span><span class="p">);</span>             
    <span class="p">};</span>

<span class="p">}]);</span></code></pre></figure>

Backend

In the server-side a JAX-RS endpoint will be receiving the RESTful request and calling an EJB to process the business operation on behalf of the user.

To learn more about JAX-RS please check this post.

JAX-RS RESTful endpoint implementation:

package io.github.guilhebl.sample.webservice.rest;

import java.io.IOException;

import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.inject.Named; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType;

@Named @RequestScoped @Path("/user") public class UserService {

<span class="nd">@EJB</span><span class="o">(</span><span class="n">mappedName</span><span class="o">=</span><span class="s">"java:global/myapp/myappEJB/UserServiceRest"</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">UserServiceRestRemote</span> <span class="n">userServiceRestRemote</span><span class="o">;</span>

<span class="kd">public</span> <span class="nf">UserService</span><span class="o">()</span> <span class="o">{</span>
<span class="o">}</span>

<span class="nd">@GET</span>
<span class="nd">@Consumes</span><span class="o">(</span><span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span>
<span class="nd">@Produces</span><span class="o">({</span><span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_XML</span><span class="o">,</span> <span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">})</span>
<span class="nd">@Path</span><span class="o">(</span><span class="s">"/reset-password/{email}"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">resetPassword</span><span class="o">(</span><span class="nd">@PathParam</span><span class="o">(</span><span class="s">"email"</span><span class="o">)</span> <span class="n">String</span> <span class="n">email</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">try</span> <span class="o">{</span>     
        <span class="n">userServiceRestRemote</span><span class="o">.</span><span class="na">resetUserPassword</span><span class="o">(</span><span class="n">email</span><span class="o">);</span>
        <span class="k">return</span> <span class="n">EncryptUtil</span><span class="o">.</span><span class="na">generateJSONStringResponse</span><span class="o">(</span><span class="n">NameConstants</span><span class="o">.</span><span class="na">SUCCESS</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">catch</span><span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="n">response</span><span class="o">.</span><span class="na">sendError</span><span class="o">(</span><span class="n">HttpServletResponse</span><span class="o">.</span><span class="na">SC_INTERNAL_SERVER_ERROR</span><span class="o">);</span> 
        <span class="o">}</span> <span class="k">catch</span><span class="o">(</span><span class="n">IOException</span> <span class="n">ioe</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">ioe</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>            
    <span class="o">}</span>
    <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>

}

In order to update the business entities update the DB, an EJB handles the business transaction calling a JPA provider to map the operation to SQL and commit the transaction if no errors are found.

EJB remote interface:

package io.github.guilhebl.sample.ejb.rest;

import javax.ejb.Remote;

import io.github.guilhebl.sample.domain.User;

@Remote public interface UserServiceRestRemote { public User findById(String id); public void resetUserPassword(String email); }

EJB implementation:

package io.github.guilhebl.sample.ejb.rest;

import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map;

import javax.ejb.EJB; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.SingularAttribute;

import io.github.guilhebl.sample.ejb.util.EncryptUtil; import io.github.guilhebl.sample.domain.User;

/* * Session Bean implementation class UserServiceRest / @Stateless(mappedName="java:global/myapp/myappEJB/UserServiceRest") public class UserServiceRest implements UserServiceRestRemote {

<span class="nd">@PersistenceContext</span>
<span class="kd">protected</span> <span class="n">EntityManager</span> <span class="n">entityManager</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">UserServiceRest</span><span class="o">()</span> <span class="o">{}</span>

<span class="kd">public</span> <span class="n">User</span> <span class="nf">findById</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span>  <span class="kd">throws</span> <span class="n">ServiceException</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">entityManager</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">User</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">id</span><span class="o">);</span>
<span class="o">}</span>

<span class="nd">@TransactionAttribute</span><span class="o">(</span><span class="n">TransactionAttributeType</span><span class="o">.</span><span class="na">REQUIRED</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">resetUserPassword</span><span class="o">(</span><span class="n">String</span> <span class="n">email</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">ServiceException</span> <span class="o">{</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="n">User</span> <span class="n">user</span> <span class="o">=</span> <span class="n">findById</span><span class="o">(</span><span class="n">email</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">user</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">user</span><span class="o">.</span><span class="na">getId</span><span class="o">()</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ServiceException</span><span class="o">(</span><span class="s">"Error: user not found for email: "</span> <span class="o">+</span> <span class="n">email</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">getActive</span><span class="o">()</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="o">!</span><span class="n">user</span><span class="o">.</span><span class="na">getActive</span><span class="o">().</span><span class="na">equals</span><span class="o">(</span><span class="n">Boolean</span><span class="o">.</span><span class="na">TRUE</span><span class="o">.</span><span class="na">toString</span><span class="o">()))</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ServiceException</span><span class="o">(</span><span class="s">"Error: user not active for email: "</span> <span class="o">+</span> <span class="n">email</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="n">String</span> <span class="n">passwordNew</span> <span class="o">=</span> <span class="n">EncryptUtil</span><span class="o">.</span><span class="na">generateRandomPassword</span><span class="o">(</span><span class="n">Apponstants</span><span class="o">.</span><span class="na">USER_GENERATE_PASSWORD_LENGTH</span><span class="o">);</span> <span class="c1">// generates an 8 length password by default</span>
        <span class="n">user</span><span class="o">.</span><span class="na">setPassword</span><span class="o">(</span><span class="n">EncryptUtil</span><span class="o">.</span><span class="na">getMD5String</span><span class="o">(</span><span class="n">passwordNew</span><span class="o">));</span> <span class="c1">// encrypt using MD5</span>
        <span class="n">entityManager</span><span class="o">.</span><span class="na">merge</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">se</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">se</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">ServiceException</span><span class="o">(</span><span class="s">"Error on reseting password for email: "</span> <span class="o">+</span> <span class="n">email</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>

}

EncryptUtils class uses java UUID as a Random ID generator:

package io.github.guilhebl.ejb.util;

import java.io.Serializable; import java.security.MessageDigest; import java.util.UUID;

public class EncryptUtil implements Serializable {

<span class="cm">/**
 * 
 */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">serialVersionUID</span> <span class="o">=</span> <span class="mi">1L</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">generateUniqueID</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">UUID</span> <span class="n">idOne</span> <span class="o">=</span> <span class="n">UUID</span><span class="o">.</span><span class="na">randomUUID</span><span class="o">();</span>
    <span class="k">return</span> <span class="n">idOne</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">getMD5String</span><span class="o">(</span><span class="n">String</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="n">MessageDigest</span> <span class="n">md</span> <span class="o">=</span> <span class="n">MessageDigest</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(</span><span class="s">"MD5"</span><span class="o">);</span>
        <span class="n">md</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">target</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
        <span class="kt">byte</span> <span class="n">byteData</span><span class="o">[]</span> <span class="o">=</span> <span class="n">md</span><span class="o">.</span><span class="na">digest</span><span class="o">();</span>

        <span class="c1">// convert the byte to hex format method 1</span>
        <span class="n">StringBuffer</span> <span class="n">sb</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuffer</span><span class="o">();</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">byteData</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">Integer</span><span class="o">.</span><span class="na">toString</span><span class="o">((</span><span class="n">byteData</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="o">)</span> <span class="o">+</span> <span class="mh">0x100</span><span class="o">,</span> <span class="mi">16</span><span class="o">)</span>
                    <span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">sb</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>

    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>  

<span class="cm">/**
 * 
 * Generates simple JSON String responses in the format:
 * 
 * {"result":"string"}
 */</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">generateJSONStringResponse</span><span class="o">(</span><span class="n">String</span> <span class="n">str</span><span class="o">)</span> <span class="o">{</span>     
    <span class="k">return</span> <span class="s">"{\"result\":"</span> <span class="o">+</span> <span class="s">"\""</span> <span class="o">+</span> <span class="n">str</span> <span class="o">+</span> <span class="s">"\"}"</span><span class="o">;</span>
<span class="o">}</span>

}



Done, now whenever user types the email on the forgot password form and clicks on Reset button, the results of the AJAX request to the server-side can be seen using a browser tool (usually the f12 key on the network tab) like:

Request URL:https://localhost/myapp/rest/user/reset-password/mytestaddress@gmail.com
Request Method:GET
Status Code:200 OK
Request Headersview source
Accept:application/json, text/plain, /
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8,pt;q=0.6
Connection:keep-alive
Cookie:JSESSIONID=67bb7a56fa98cf92ceb15a8105aa
Host:localhost
Referer:https://localhost/app/index.html
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.94 Safari/537.36
Response Headersview source
Connection:keep-alive
Content-Length:7
Content-Type:application/json
Date:Fri, 20 Feb 2015 10:16:19 GMT
Server:nginx/1.6.2 (Ubuntu)
X-Powered-By:Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.1  Java/Oracle Corporation/1.8)

{"response": "success"}


Now it is convenient to check in the DB the results of the opration, if user password has changed accordingly. I will not cover the DB configuration part of it, since any JPA compatible JDBC driver can connect to a specific DB server instance.

In overall this sample depicts a typical implementation of both Frontend and Backend of a user re-generate password scenario, using Angular and JAX-RS.


comments powered by Disqus