Category Archives: REST

User registration and social logins are extremely vital for any web application. It is the first step towards building any app, as the most basic functionality on any app would be a ‘user registration’ or logging in using ‘social accounts’. But, building an app with even these features takes a lot of time. I’m afraid, at least a week. So, we found out a solution for this. Our hosted API service lets you build an app with creating and managing user accounts along with a social login registration.

In this blog post, we’ll walk you through a tutorial where you can learn how to build a Ruby on Rails app with our Rest API with basic functionalities like login/registration, edit profile and more. And the best part? It takes just 15 minutes only.

You can click here to know more about our API.

To interact with the NBOS API Server, I have created a Ruby gem called “wavelabs_client_api“. The source code and for an easy reference, the documentation of this gem are available here. This gem is published in rubygems.org under the MIT license.

Prerequisites
1.Install Ruby Version Manager(RVM).
2.Install the Ruby 2.2.3 & Rails 4.2.4 using RVM.

Detailed installation instructions are available here

Note: No database is required because our API server has got it covered.

Now, let us create the web application. Open a console and follow the instructions:.

 $>git clone https://github.com/nbostech/wavelabs-rails-client-api.git
 $>cd wavelabs-rails-client-api
 $>git checkout -b release-1 v0.1
 $>bundle install
 $>rails server

Open the browser and access your application.The home page would look like this.

starter_app_home_page

Now you can use the following functionalities using the above application:
1. Signup
2. Login and Logout
3. Edit Profile Information
4. Change Password
5. Upload Profile Picture
6. Login with Facebook
7. Login with Github
8. Login with LinkedIn
9. Login with Google plus
10. Login with Instagram

The live application is available at Heroku Server

And there, you are all set to go. You haven’t just built a Ruby on Rails with basic login and registration functionality within 15 minutes, you are also all set to launch it into the market.

If you have any questions, feel free to post your comments and we’ll get back to you.

 

Ever wondered if you could build an iOS application with basic functionalities as registration and social logins under a limited amount of time? On an average, it takes at least a week for us to build an iOS application and integrate registration and login functionalities with it. Why spend so much time on integrating those features into your app when you could focus on your product? Our iOS client library helps you build one under a matter of hours. It is a simple and secure user management for iOS developers. And what’s not to love? It’s easy to integrate with an iOS app.

In this post, I’ll walk you through building the iOS app seamlessly.

Requirements

  • iOS 8.0+ / Mac OS X 10.9+
  • Xcode 6.4

Installation

CocoaPods – CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects.

You can install CocoaPods tool on OS X by running the following command using the terminal.

Add the Wavelabs iOS Client Library to your iOS app

1) Create an Xcode Project on the local machine.

2) Open a terminal and cd to the directory containing the project.

3) Add your dependencies in the Podfile as shown here.

4) Save the file.

5) Run the command – pod install. This will install the SDK’s specifier in Podfile and dependencies that framework contains.

6) Open your app’s .xcworkspace file.

Usage

Register in api.wavelabs.in. Create a tenant (or) workspace and generate a clientID.

Add Baseurl and clientId in Targets/info as WavelabsAPISettings

WavelabsSettings_ClientID

Sample ‘AuthApi‘ Request:

AuthApi is using the custom delegate ‘getAuthApiResponseDelegate’, and wherever we want to use AuthApi, we need to set the AuthApi delegate.

User Login

As you probably know, eCommerce portal with OAuth2 development can be broken up into three main areas:

  • Setup eCommerce portal using OpenCart
  • Installing vQmod extension for OpenCart
  • Add REST API with OAuth2.0 to OpenCart

Setup eCommerce portal using OpenCart

Setup eCommerce portal using OpenCart. Here is a link for reference.

Installing vQmod extension for OpenCart

“vQmod” (aka Virtual Quick Mod) is an override system designed to avoid having to change OpenCart core files.

How to install vQmod using Autoinstaller

1) Download the latest version that has “opencart” in the title from the download area.

  1. Using FTP, upload the “vqmod” folder from the zip to the root of your opencart store.
  2. Be sure the vqmod folder and the vqmod/vqcache folders are writable (either 755 or 777).
    • Also be sure index.php and admin/index.php are writable.
      • If you’re not sure, then try 755.
      • If you get errors about permissions, then try 777.

NOTE: 777 permissions are dangerous and should only be used as a last resort. If your hosting requires this, consult your hosting to let them know this shouldn’t be necessary

  1. Goto http://www.yoursite.com/vqmod/install
  2. You should get a success message. If not, check the above permissions, and try again
  3. Load your store homepage and verify that it works.
  4. Using FTP, verify that there are new “vq” files in the “vqmod/vqcache” folder.
  5. If yes, then you are ready to start downloading or creating vQmod scripts, otherwise ask for assistance.

Done!

  • DO NOT DELETE THE INSTALL FOLDER!
  • YOU MUST RUN THE INSTALLER EVERY TIME YOU UPGRADE OPENCART!!
  • THERE IS NO DANGER OF RE-RUNNING THE INSTALLER!

How to install vQmod manually?

1) Download the latest version that has “OpenCart” in the title.

  1. Using FTP, upload the “vqmod” folder from the zip to the root of your OpenCart store.
  2. Be sure the vqmod folder and the vqmod/vqcache folders are writable (either 755 or 777).
    • Also be sure index.php and admin/index.php are writable.
      • If not sure which you need, first try 755.
      • If you get errors about permissions, then try 777.
  3. Edit your index.php file.
  4. FIND

  5. REPLACE WITH

Note the affiliate library file may not exist on older systems. Basically any require_once(DIR_SYSTEM . 'library/xxxxxxxx.php'); needs to be changed to use the VQMod::modCheck above in the same format. This also applies to any additional require_once files in the next step

  1. Edit your admin/index.php file
  2. FIND

  3. REPLACE WITH

  4. Load your store homepage and verify if it works.
  5. Using FTP, verify that there are new “vq” files in the “vqmod/vqcache” folder.
  6. If yes, then you are ready to start downloading or creating vQmod scripts.

Done!

Add REST API with OAuth2.0 to OpenCart

OpenCart Rest API is a full featured API that allows you to set up your own rest services within minutes.

You can download it here.
The upload folder contains all files for this module.
You will notice that the folders are in the same structure as your OpenCart installation.

1. Navigate to your opencart root folder using an FTP program.
2. Upload all folders of extension to your opencart installation folder.
3. Execute these SQL statements in phpMyAdmin or whatever client you use for managing your database.

4. Go to your admin area in Extensions->Modules and enable your REST API extension.
You have to fill the Order id field (you can find it in the order email)
You have to fill the client id, client secret fields and TTL. This is needed for authentication with the API.

5. Paste into your opencart root folder .htaccess file after “RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]”
RewriteRule .* – [E=HTTP_Authorization:%{HTTP:Authorization}]

6. Paste into your opencart root folder .htaccess file after “RewriteBase /”

Now we’re able to run Mobile Apps using our own E-Commerce website REST and OAuth2.0 API.

The intent is to implement OAuth2 provider protocol & secure appropriate rest api’s using OAuth2 token.

The generic need for any application would be to have some public API’s that can be accessed without any security, & some secured using token associated with clientId/secretKey & some secured using token associated with user credentials.

The following are the rest endpoints supporting OAuth2, which provide a token using different protocols, i.e Authorization Code Grant flow (1a,1b), Implicit Grant flow (2), Resource Owner Password Grant (3), Client Credentials Grant (4), Refresh Token Grant(5).

  • OAuth2 Rest End Points 
      1. [ GET] /oauth/authorize:
        • (1a) OAuth2 Authorization Code Grant
          parameters [ ?response_type=code&client_id=my-client&scope=read ]
        • (2) OAuth2 Implicit Grant
          parameters [ ?response_type=token&client_id=my-client&scope=read ]
      2. [ POST] /oauth/token:
            • (1b) OAuth2 Authorization Code Grant
              parameters [ ?grant_type=authorization_code&code=139R59&client_id=my-client ]
            • (3) OAuth2 Resource Owner Password Grant.
              parameters [ ?grant_type=password&client_id=..&client_secret=my-secret&username=aa&password=bb&scope=read ]
            • (4) OAuth2 Client Credentials Grant
              parameters [ ?grant_type=client_credentials&client_id=my-client&scope=read ]
            • (5) OAuth2 Refresh Token Grant
              parameters [ ?grant_type=refresh_token&refresh_token=22c3f395-690c-4838-a14f-3137e6802db1&client_id=my-client&scope=read ]

The following are the API endpoints we would like to support using the token provided above

    1. Public API’s
      1. [  GET] /api/v0/about
      2. [  GET] /api/v0/sample/about
    2. Secured access using a Client Token
      1. [  GET] /api/v0/sample/securedWithClientOAuth : Sample API secured with client token
      2. [POST] /api/v0/members/signup: Create a new User
      3. [POST] /api/v0/auth/login: Login using username/password credentials
    3. Secured access using User Token
      1. [  GET] /api/v0/sample/securedWithUserOAuth: Sample Secured with user token
      2. [  GET] /api/v0/members: Get list of members supports pagination ?filter=”field1=value,field2=v”&sort=”field2″&offset=15
      3. [  GET] /api/v0/members/{memberId}: Get information about a user
      4. [  PUT] /api/v0/members/{memberId}: update a user
      5. [  GET] /api/v0/auth/logout: logout the user associated with the token

In addition, we would want the api server to respond with the following standard HTTP Status responses across all api calls

  1. HTTP 200: Any successful REST api call along with its specific json data
  2. HTTP 400: Any api that results in validation errors like “invalid username”, “empty password” etc. along with json data with list of property errors
    “{ [propertyName:””, messageCode:””, message:””], [propertyName:””,messageCode:””].. }
  3. HTTP 404: Any non-existent REST api request using json along with json response of
    { messageCode:”api.notfound”, message:”locale based message desc”}
  4. HTTP 500: Any REST api request resulting in internal error along with json response of
    {messageCode:”api.internal.error”, message:”locale based message” }
  5. HTTP 403: Any REST api request resulting in forbidden
  6. HTTP 405: Any REST api request using invalid method i.e using GET instead of POST/PUT

Any REST API that supports pagination should respond data in the following format, with results containing the appropriate list items and accept “filter”,”offset”,”sort” & “order” as parameters.

{ 
      "from":10, 
        "to":15, 
"totalCount":100,
     results: [
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" }
     ]
}

Here is sample input/output for the api endpoints for successful responses

Input Output
[POST] /api/v0/members/signup: Create a new User
curl –X POST –d ‘{
“clientId”:”my-client”,
“username”:”abcdef”,
“password”:”123456″,
“email”:”abcd@ef.com”,
“firstName”:”abcd”
}’
{“member”: {
“description”: “”,
“email”: “abcd@ef.com”,
“firstName”: “abcd”,
“id”: 5,
“lastName”: “”,
“phone”: 0,
“socialAccounts”: []
},
“token”: {
“access_token”: “7224..”,
“expires_in”: 43199,
“refresh_token”: “ebff..”,
“scope”: “read,write”,
“token_type”: “bearer”
} }
[POST] /api/v0/auth/login: Member Login
curl –X POST –d ‘{
“clientId”: “my-client”,
“username”: “abcdef”,
“password”: “123456”
}’
{“member”: {
“description”: “”,
“email”: “abcd@ef.com”,
“firstName”: “abcd”,
“id”: 5,
“lastName”: “”,
“phone”: 0,
“socialAccounts”: []
},
“token”: {
“access_token”: “7224..”,
“expires_in”: 43199,
“refresh_token”: “ebff..”,
“scope”: “read,write”,
“token_type”: “bearer”
} }
[  GET] /api/v0/members/{memberId}: Get Member
curl -H     “Authorization: Bearer 7224…” http://../api/v0/members/5 {
“description”: “”,
“email”: “abcd@ef.com”,
“firstName”: “abcd”,
“id”: 5,
“lastName”: “”,
“phone”: 0,
“socialAccounts”: []
}
[  PUT] /api/v0/members/{memberId}: Update Member
curl –X PUT –H “Authorization: Bearer 7224…”
-d { “id”: 5, “firstName”: “fname”, “lastName”: “lname”,”phone”: 0, “description”: “desc”} http://../api/v0/members/5
{
“description”: “desc”,
“email”: “abcd@ef.com”,
“firstName”: “fname”,
“id”: 5,
“lastName”: “lname”,
“phone”: 0,
“socialAccounts”: []
}


We will walk thru the implementation in the following sequence.

      1. Implement OAuth2 endpoints using OAuth2 Server library along with sample endpoints access controlled by client/user tokens
      2. Annotate your API with Swagger-UI
      3. Create API for Member Create/Read/Update
      4. Write Unit tests for Member Create/Read/Update
      5. Create API for Auth login/logout & Write Unit Tests

1. Create API for Member Create/Read/Update

Lets make a list of features that we want, just so it would guide us in our design.

  Feature
Login Domain
1a Ensure that password is getting encrypted while saving
1b We shouldn’t be able to create new Login with same username
1c We should have the ability to expire an account
1d We should have the ability to lock an account
Member Domain
2a Each member should have a unique UUID to identify them using URL
2b We cannot have two members with same email
2c A Member cannot exist without a Login
API / MemberController / create
3a Registration with an existing username should respond with 400
3b Registration with an existing email should respond with 400
3c Invalid email syntax should return 400
3d Successful registration should respond with 200 along with member info

Given the list of features, we will start off with the following ER Model

login-member-ermodel

If you are looking for PHP implementation, here is the link

If you are looking for Grails implementation, here is the link

In this post, we’ll learn how to deploy and use REST OAuth2.0 API server using PHP.
Here, REST API using OAuth2

The generic need for any application would be to have some public API’s that can be accessed without any security, & some secured using token associated with clientId/secretKey & some secured using token associated with user credentials.

We will walk thru the implementation in the following sequence.

      1. Implement OAuth2 endpoints using PHP OAuth2 Server library along with sample endpoints access controlled by client/user tokens
      2. Annotate your API with Swagger-UI
      3. Create API for Member Create/Read/Update
      4. Write Unit tests for Member Create/Read/Update
      5. Create API for Auth login/logout & Write Unit Tests

Before we start ensure that your environment is all set

      • Install XAMPP
      • Create a starter app using starter-app-php

        Create a schema named starter_app_dev in mysql using phpMyAdmin.
        Copy code into starter-app-php folder
        Now ready http://localhost/starter-app-php/ app

1 viagra est il en vente libre. Create API for Member Create/Read/Update

Lets make a list of features that we want, just so it would guide us in our design.

  Feature
Login Domain
1a Ensure that password is getting encrypted while saving
1b We shouldn’t be able to create new Login with same username
1c We should have the ability to expire an account
1d We should have the ability to lock an account
Member Domain
2a Each member should have a unique UUID to identify them using URL
2b We cannot have two members with same email
2c A Member cannot exist without a Login
API / MemberController / create
3a Registration with an existing username should respond with 400
3b Registration with an existing email should respond with 400
3c Invalid email syntax should return 400
3d Successful registration should respond with 200 along with member info

Given the list of features, we will start off with the following ER Model

login-member-ermodel

Our next step is to create a module classes in CI.

class MemberModel extends CI_Model
{

    function __construct()
    {
        parent::__construct();
    }

    function addLogin($pdata)
    {
        $this->db->insert("login", $pdata);
        return $this->db->insert_id();
    }

    function addMember($pdata)
    {
        $this->db->insert("member", $pdata);
        return $this->db->insert_id();
    }

    function updateMemberById($id, $pdata)
    {
        $this->db->where("id", $id);
        return $this->db->update("member", $pdata);
    }

    function updateMemberByLoginId($login_id, $pdata)
    {
        $this->db->where("login_id", $login_id);
        return $this->db->update("member", $pdata);
    }

    function getLoginByUsername($username)
    {
        $this->db->select("m.*");
        $this->db->where("m.username", $username);
        $query = $this->db->get("login m");
        if ($query->num_rows() > 0) {
            return $query->row_array();
        }
        return false;
    }

    function getMemberById($login_id)
    {
        $this->db->select("u.*");
        $this->db->where("m.id", $login_id);
        $this->db->join("member u", "m.id=u.login_id");
        $query = $this->db->get("login m");
        if ($query->num_rows() > 0) {
            return $query->row_array();
        }
        return false;
    }

    function getMemberByLoginId($login_id)
    {
        $this->db->select("u.*");
        $this->db->where("m.id", $login_id);
        $this->db->join("member u", "m.id=u.login_id");
        $query = $this->db->get("login m");
        if ($query->num_rows() > 0) {
            $row = $query->row_array();            
            return $row;
        }
        return false;
    }

    function checkUsername($username)
    {
        $this->db->select("m.*");
        $this->db->where("m.username", $username);
        $query = $this->db->get("login m");
        if ($query->num_rows() > 0) {
            return true;
        }
        return false;
    }

}

Lets Create our first API to create a Member
Add an action to create Member.

include_once(APPPATH . "libraries/OAuth2/Autoloader.php");

class Member extends BaseApi
{
    function __construct()
    {
        // Construct the parent class
        parent::__construct();
        $this->load->library('oauth');
        $this->load->model("MemberModel", "memberModel");
    }

    function create_post()
    {
        $this->server = $this->oauth->getOauthServer();
        $request = OAuth2\Request::createFromGlobals();
        $request->request['response_type'] = "token";
        if (empty($request->request['state'])) {
            $request->request['state'] = "create";
        }
        $response = new OAuth2\Response();

        if (!$this->server->validateAuthorizeRequest($request, $response)) {
            $this->set_response_error($response->getParameters());
        } else {
            $errors = [];
            if (!empty($this->post('username')) && $this->apiModel->checkUsername($this->post('username')) === true) {
                $errors[] = [
                    "messageCode" => "user.username.exists",
                    "message" => "user.username.exists",
                    "objectName" => "",
                    "propertyName" => "username"
                ];
            } else if (empty($this->post('username'))) {
                $errors[] = [
                    "messageCode" => "user.username.invalid",
                    "message" => "user.username.invalid",
                    "objectName" => "",
                    "propertyName" => "username"
                ];
            }
            if (empty($this->post('password'))) {
                $errors[] = [
                    "messageCode" => "user.password.invalid",
                    "message" => "user.password.invalid",
                    "objectName" => "",
                    "propertyName" => "password"
                ];
            }
            if (empty($this->post('firstName'))) {
                $errors[] = [
                    "messageCode" => "user.firstName.invalid",
                    "message" => "user.firstName.invalid",
                    "objectName" => "",
                    "propertyName" => "firstName"
                ];
            }

            if (empty($errors)) {
                $login_data = array();
                $login_data['username'] = $this->post('username');
                $login_data['password'] = $this->post('password');
                $login_data['password_expired'] = 0;
                $login_data['account_expired'] = 0;
                $login_data['account_locked'] = 0;
                $login_data['enabled'] = 1;
                $login_id = $this->apiModel->addLogin($login_data);
                if ($login_id !== false) {
                    $member_data = array();
                    $member_data['login_id'] = $login_id;
                    $member_data['first_name'] = ($this->post("firstName") !== null) ? $this->post("firstName") : "";
                    $member_data['last_name'] = ($this->post("lastName") !== null) ? $this->post("lastName") : "";
                    $member_data['email'] = ($this->post("email") !== null) ? $this->post("email") : "";
                    $member_data['phone'] = ($this->post("phone") !== null) ? $this->post("phone") : "";
                    $member_data['description'] = ($this->post("description") !== null) ? $this->post("description") : "";
                    $member_id = $this->apiModel->addMember($member_data);
                }
            }

            if (empty($errors)) {
                $member = $this->apiModel->getMemberByLoginId($login_id);
                $this->server = $this->oauth->getOauthServer(["password"]);
                $request = OAuth2\Request::createFromGlobals();
                $request->request['grant_type'] = "password";
                $token = $this->server->handleTokenRequest($request)->getParameters();

                $this->set_response_success([
                    "member" => $member,
                    "token" => $token
                ]);
            } else {
                $this->set_response_error([
                    "errors" => $errors
                ]);
            }
        }
    }

    function login_post()
    {
        $this->server = $this->oauth->getOauthServer(["password", "refresh_token"]);
        $request = OAuth2\Request::createFromGlobals();
        $request->request['grant_type'] = "password";
        $token = $this->server->handleTokenRequest($request)->getParameters();
        $member_data = [];
        if(!empty($token['user_id'])){
            $member_data = $this->apiModel->getMemberByLoginId($token['user_id']);
        }
        if (!empty($token['access_token'])) {
            $this->set_response_success([
                "member" => $member_data,
                "token" => $token
            ]);
        } else {
            $this->set_response_error($token);
        }
    }
    
}

 

We will continue this post with creating tests for the features that we have built.

What is Swagger:

Swagger is a annotations library for generating Swagger compatible JSON documentation for your API. The resulting JSON documentation may then be utilized for internal and external user friendly documentation, API portal sandbox with Swagger UI.

You might have an existing application exposing some API in your favorite PHP Framework.
Irrespective of your framework, Swagger can be integrated using Annotation.
Refer Swagger Annotations

PreRequisite:
a) PHP command should work in your console terminal or windows command line tool.
b) Use php -c FILE_PATH_OF_php.ini in case php command gives any error

Now its time to open terminal console and generate Swagger UI. Here are the steps to integrate:

  1. Download Swagger.phar
  2. Add swagger Annotation to PHP file
  3. Generate Swagger JSON files
  4. Install Swagger UI Project
  5. Open Swagger UI

1. Download Swagger.phar

Create a folder for Swagger generator. Name it as SwaggerGenerator.
Download composer.phar file from Composer Project.
Next run below line in console :

After running above command, you should have swagger.phar in SwaggerGenerator/vendor/zircote/swagger-php/swagger.phar.

Copy that file and move it to SwaggerGenerator/swagger.phar

2. Add Swagger Annotation to PHP file

Sample PHP file with Swagger Annotation.
Add this Annotation as comments to your Controller or Class files as per your requirements.

3. Generate Swagger JSON files

Create a folder in your Apache DirectoryRoot called Swagger and a subfolder docs. Swagger/docs

4. Install Swagger UI project

Download the swagger UI code from here and place it in  Swagger/ui folder.

Edit ui/index.html and change the default  URL in javascript so that it picks up your domain.

 

5. Open Swagger UI

Go to browser and try to access “http://localhost/swagger/ui”. You will see below  screens on the browser.

Screen Shot 2016-01-12 at 3.30.41 PM

In this article, we will learn how to integrate Swagger-UI with Grails application. Basically ‘SwaggerUI’ is used to serve documentation of a REST API. It describes and visualises RESTful web services, not only that it will help to expose the REST APIs to end users. With Swagger-UI we can represent our APIs in graphical representation. In this article, we are going to integrate Swagger-UI into sample-oauth2-app. Our aim is to see the following pages.

swagger-ui-5

swagger-ui-6

Here are the steps to integrate Swagger-UI:

  1. Add swagger dependencies
  2. Configure the Swagger
  3. Writing a controller that enables the swagger into action
  4. Annotate API with swagger annotations
  5. Add swagger UI plugin

1. Add Swagger Dependencies

In this initial step, we need to add swagger dependencies in grails-app/conf/BuildConfig.grooy

compile "io.swagger:swagger-core:1.5.3"
compile "io.swagger:swagger-jaxrs:1.5.3"

2.Configure the Swagger

Swagger’s BeanConfig class allows you to set various properties for Swagger’s initialization. Configure the BeanConfig class in resource.groovy

Method Property Name Purpose
setTitle(String) title Sets the title of the application.
setDescription(String) description Sets the description of the application.
setTermsOfServiceUrl(String) termsOfServiceUrl Sets the URL of the application’s Terms of Service.
setContact(String) contact Sets the contact information for the application.
setLicense(String) license Sets the license of the application
setLicenseUrl(String) licenseUrl Sets the license URL of the application.
setVersion(String) version Sets the version of the API.
setBasePath(String) basePath Sets the basePath for the API calls.
setApiReader(String) apiReader Sets an API Reader class for Swagger.
setFilterClass(Sting) filterClass Sets a security filter for Swagger’s documentation.
setResourcePackage(String) resourcePackage Sets which package(s) Swagger should scan to pick up resources. If there’s more than one package, it can be a list of comma-separated packages
setScan(boolean) scan When set to true, Swagger will build the documentation
import io.swagger.jaxrs.config.BeanConfig
beans = {
    swaggerConfig(BeanConfig) {
        def serverUrl = grailsApplication.config.grails.serverURL.toString()
        def hostName = serverUrl.substring(serverUrl.indexOf("://")+3)
        resourcePackage = 'com.nbos.core'
        host = hostName
        basePath = "/api"
        version = 'v0' // Default "1".
        title = 'Core Registration API, Version V0' // Default: App Name.
        description = 'API for Accessing secured resources'
        contact = 'webservices@nbostech.com'
        license = ''
        licenseUrl = ''
    }
}

3. Writing a Controller that Enables the Swagger into Action

After the configuration, we have to write controller to get the swagger into action

package com.nbos.apidoc
import org.codehaus.groovy.grails.web.mapping.LinkGenerator

class ApiDocController {
    LinkGenerator grailsLinkGenerator
    def apiDocService
    def index = {
        String basePath = grailsLinkGenerator.serverBaseURL
        render(view: 'index', model: [apiDocsPath: "${basePath}/api/swagger-json"])
    }
    def swaggerJson = {
        render apiDocService.generateJSON()
    }
}

Map these actions in URL mappings.groovy

> grails url-mappings-report
Dynamic Mappings
 |    *     | /oauth/authorize                                          | Action: (default action)                 |
 |    *     | /oauth/token                                              | Action: (default action)                 |
 |    *     | /${controller}/${action}?/${id}?(.${format)?              | Action: (default action)                 |
 |    *     | /                                                         | View:   /index                           |
 |    *     | ERROR: 500                                                | View:   /error                           |

Controller: apiDoc
 |    *     | /api/info                                                 | Action: index                            |
 |    *     | /api/swagger-json                                         | Action: swaggerJson                      |

Controller: dbdoc
 |    *     | /dbdoc/${section}?/${filename}?/${table}?/${column}?      | Action: (default action)                 |

Controller: sample
 |    *     | /api/sample/securedWithClientOAuth                        | Action: securedWithClientOAuth           |
 |    *     | /api/sample/securedWithUserOAuth                          | Action: securedWithUserOAuth             |

For Swagger to actually produce the documentation, you must setScan(true). The BeanConfig should be called when your application starts up. Invoke the BeanConfig in service class

package com.care.apidoc
import io.swagger.jaxrs.config.BeanConfig
import io.swagger.util.Json;
import grails.transaction.Transactional

@Transactional
class ApiDocService {

    def swaggerConfig

    /*
     * generates SWAGGer JSON
     */
    def generateJSON() {

        String[] schemes = ["http"] as String[]
        swaggerConfig.setSchemes(schemes)
        swaggerConfig.setScan(true)
        def swagger = swaggerConfig.getSwagger()

        Json.mapper().writeValueAsString(swagger);
    }
}

4. Annotate API with Swagger Annotations

We are annotating the SampleController APIs with Swagger annotations, that will help to generate SwaggerJson.

package com.nbos.core
import grails.plugin.springsecurity.annotation.Secured
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation

import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces

@Path("/sample")
@Api(value = "auth", description = "Secured Access Using tokens")
@Produces(["application/json", "application/xml"])
class SampleController {

    static responseFormats = ['json', 'xml']

    @GET
    @Path("/securedWithClientOAuth")
    @ApiOperation(value = "Secured Access Using client tokens", httpMethod = "GET" )
    @Secured("#oauth2.isClient()")
    def securedWithClientOAuth() {
        def map=['sucsess':'AuthenticationSucces','message':'Client is authenticated successfully']
        respond map
    }
    @GET
    @Path("/securedWithUserOAuth")
    @ApiOperation(value = "Secured access using user token", httpMethod = "GET" )
    @Secured("#oauth2.isUser()")
    def securedWithUserOAuth(){
        def map=['sucsess':'AuthenticationSucces','message':'User is authenticated successfully']
        respond map
    }
}

5. Add swagger UI plugin

Lastly, we need to add Swagger UI plug. You can Download swagger UI, copy and paste it in folder/web app. Now write the index.html apidoc/index.gsp

Swagger UI

    %{--<!-- TODO: move these to use asset-pipeline -->--}%

    <script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'jquery-1.8.0.min.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'jquery.slideto.min.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'jquery.wiggle.min.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'jquery.ba-bbq.min.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'handlebars-2.0.0.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'underscore-min.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'backbone-min.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2', file: 'swagger-ui.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'highlight.7.3.pack.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'marked.js')}"></script><script src="${resource(dir: 'vendor/swagger-ui/2.1.4-M2/lib', file: 'swagger-oauth.js')}"></script>%{--<!-- enabling this will enable oauth2 implicit scope support -->--}% %{--<script></script>--}%<script>// <![CDATA[         $(function () {             window.swaggerUi = new SwaggerUi({                 url: "${apiDocsPath}",                 dom_id: "swagger-ui-container",                 supportedSubmitMethods: ['get', 'post', 'put', 'delete'],                 onComplete: function(swaggerApi, swaggerUi){                     if(typeof initOAuth == "function") {                         /*                          initOAuth({                          clientId: "your-client-id",                          realm: "your-realms",                          appName: "your-app-name"                          });                          */                     }                     $('pre code').each(function(i, e) {                         hljs.highlightBlock(e)                     });                 },                 onFailure: function(data) {                     console.log("Unable to Load SwaggerUI");                 },                 docExpansion: "none"             });             $('#input_apiKey').change(function() {                 var key = $('#input_apiKey')[0].value;                 if(key && key.trim() != "") {                     swaggerUi.api.clientAuthorizations.add("key", new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + key, "header"));                 }             })             window.swaggerUi.load();         });      // ]]></script></pre>
<div id="header">
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id="api_selector">
<div class="input"><input id="input_baseUrl" name="baseUrl" type="text" placeholder="http://example.com/api" /></div>
<div class="input"><input id="input_apiKey" name="apiKey" type="text" placeholder="access_token" /></div>
<div class="input"><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap"></div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
<pre>

 

Now we all are set. Run the server.

> grails run-app

Go to browser and try to access “http://localhost:8080/sample-oauth2-app/api/info”. You will see above screens on the browser.

What is OAuth2

OAuth2 is a authorisation framework that provides a simple mechanism for end users to grant a third party access to their resource without providing a username and password.It also enable the user to grant limited access to their data in terms of scope ,duration etc.Typically client send username and password to the server ,for access the resources.Instead of doing this typical authentication process what client/user will do with OAuth2 is ,client sends request to oauth2 server(authentication server) it will validate the user request and respond with a accesstoken .With that access token client access the resources.Here the steps how client communicate with server

  1. User log in to the oauth2 server(authentication server)
  2. Obtain the Token
  3. Access protected resource with the Token
  4. Server check the Token
  5. If token is valid token allow the user to access protected resources

oauth2
Here we are going to implement Sample OAuth2 application with grails

 

1.Create App

create a sample app using grails

> grails create-app sample-oauth2-app
 cd sample-oauth2-app/
> grails run-app

2.Configure Spring Security Oauth2 Provider

2.1 Add spring-security-oauth2-provider dependency in grails-app/conf/BuildConfig.groovy

plugin{
 compile "org.grails.plugins:spring-security-oauth2-provider:2.0-RC1"
}

Then compile the App

> grails compile

This will install the oauth2 provider in your application.OAuth2 provider defines two endpoints.
OAuth endpoints are the URLs we use to make OAuth authentication requests.We need to use the correct OAuth endpoint when issuing authentication requests in your application

  • Authorization endpoint
  • Token endpoint

Authorization endpoint
The authorization end point is an an endpoint on the authorisation server where the resorce owner login in and get authentication to client application.

Token endpoint
Token endpoint is the endpoint on the server where the client application exchange the client_id and client secret ,for an access token.
In this blog we are going to concentrate on Token endPoints.
The following are the rest token endpoints supporting OAuth2 protocols, which provide a token using various protocols.

[ POST] /oauth/token:

  • OAuth2 Authorization Code Grant
    parameters[?grant_type=authorization_code&code=139R59&client_id=my-client ]
  • OAuth2 Resource Owner Password Grant.
    parameters [ ?grant_type=password&client_id=..&client_secret=my-secret&username=aa&password=bb&scope=read ]
  • OAuth2 Client Credentials Grant
    parameters [ ?grant_type=client_credentials&client_id=my-client&scope=read ]
  • OAuth2 Refresh Token Grant
    parameters [ ?grant_type=refresh_token&refresh_token=22c3f395-690c-4838-a14f-3137e6802db1&client_id=my-client&scope=read ]

Each OAuth flow defines which endpoints you need to use and what request data you need to provide.

Here you can see dynamic mappings of end points.

> grails url-mappings-report

| URL Mappings Configured for Application
| ---------------------------------------

Dynamic Mappings
 |    *     | /oauth/authorize                                          | Action: (default action)                 |
 |    *     | /oauth/token                                              | Action: (default action)                 |
 |    *     | /${controller}/${action}?/${id}?(.${format)?              | Action: (default action)                 |

2.2 Create Domain classes

Execute grails s2-init-oauth2-provider <package> <client-name> <authorization-code-name> <access-token-name> <refresh-token-name>
eg:

> grails s2-init-oauth2-provider com.nbos.oauth OAuthClient OAuthAuthorizationCode OAuthAccessToken OAuthRefreshToken 

This will create Domain classes for you in specified domain package and update the grails-app/conf/Config.groovy so the plugin recognize them.

  • Client
  • AUthorizationCode
  • AccessToken
  • RefreshToken

Client;
This class represent Client details like client_seceret,clientId,clinet scope,client authorities

AuthorizationCode
This class represents an authorization code that has been issued via the authorization endpoint as part of an authorization code grant. The authentication object serialized is an instance of OAuth2Authentication from Spring Security OAuth.

AccessToken
This class represents an access token than has been issued to a client on behalf of a user. The authentication object serialized is an instance of OAuth2Authentication from Spring Security OAuth.

RefreshToken
This class represents a refresh token issued as part of one of the grants that supports issuing a refresh token. The length of time the refresh token is valid is determined by the token services and can be configured.

Updated lines in grails-app/conf/Config.groovy

grails.plugin.springsecurity.oauthProvider.clientLookup.className = 'com.nbos.oauth.OAuthClient'
grails.plugin.springsecurity.oauthProvider.authorizationCodeLookup.className = 'com.nbos.oauth.OAuthAuthorizationCode'
grails.plugin.springsecurity.oauthProvider.accessTokenLookup.className = 'com.nbos.oauth.OAuthAccessToken'
grails.plugin.springsecurity.oauthProvider.refreshTokenLookup.className = 'com.nbos.oauth.OAuthRefreshToken

2.3 Exclude client secret from Logs

When grails logs a stacktrace ,the log message may include all the names and values of request parameters for the current request.To mask out the values of secure request parameters , specify the parameters name in the grails.exceptionresolver.params.exclude config property.
Update the params exlusion list in grails/config/Config.groovy , so the client secrets are not logged in clear

> grails.exceptionresolver.params.exclude = ['password', 'client_secret']

2.4 Sample Client

Before our(client) application can access oauth2 resources we should resister the client with the ouath2.It’s similar to creating clientId for our app in Google plus/facebook .The registration is typically a one-time task. Once registered, the registration remains valid.

For example, you can register a client in grails-app/conf/Bootstrap.groovy as follows:

 def init = { servletContext ->
        new OAuth2Client(
                clientId: 'my-client',
                clientSecret: 'my-secret',
                authorizedGrantTypes: ['authorization_code', 'refresh_token', 'implicit', 'password', 'client_credentials'],
                authorities: ['ROLE_CLIENT'],
                scopes: ['read','write'],
                redirectUris: ['http://localhost:8080/sample-oauth2-app']
        ).save(flush: true)

    }

2.5 Configure the filter chain

Spring Security maintains a filter chain internally where each of the filters has a particular responsibility and filters are added or removed from the configuration depending on which services are required. The ordering of the filters is important as there are dependencies between them.Spring Security infrastructure is based entirely on standard servlet filters.We can exclude some default filters with chain map.

grails.plugin.springsecurity.filterChain.chainMap = [
        '/oauth/token': 'JOINED_FILTERS,-oauth2ProviderFilter,-securityContextPersistenceFilter,-logoutFilter,-rememberMeAuthenticationFilter',
        '/**': 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter',
]

Run the app,You can observe what ever url you give it will navigate to /login/auth .This is because of ‘staticRules’ Map

grails.plugin.springsecurity.controllerAnnotations.staticRules = []

To avoid this ,comment ‘staticRules’ Map and add below two configurations in config.groovy

grails.plugin.springsecurity.rejectIfNoRule = false
grails.plugin.springsecurity.fii.rejectPublicInvocations = false

If you set rejectIfNoRule or rejectPublicInvocations to true we’ll need to configure the staticRules map to include URLs that can’t otherwise be guarded.

2.6 Register Authentication Providers

The Spring security plugin register Authentication providers that will perform authentication.Add below lines in grails-app/conf/config.groovy

grails.plugin.springsecurity.providerNames = [
        'clientCredentialsAuthenticationProvider',
        'daoAuthenticationProvider',
        'anonymousAuthenticationProvider',
        'rememberMeAuthenticationProvider'
]

This is the time to run the app and test the application

grails run-app

3.Sample input/output on token endpoint

3.1 Client credentials grant

The client credentials grant is performed by authenticating the client via token endpoint.This is the client we loaded in grails-app/conf/bootstrap.groovy

> curl –X POST –d {
"grant_type":"client_credentials",
"client_id":"my-client",
"client_secret":"my-secret",
"scope":"read"
}
http://localhost:8080/sample-oauth2-app/oauth/token

The access_token come in json response

{
access_token: "3c27a91f-4753-425e-aa6e-6f8920a21d42"
token_type: "bearer"
expires_in: 43199
scope: "read"
}

3.2 Resource Owner Grant

Create a domain classes in grails.

grails create-domain-class com.nbos.core.Login

Add the necessary properties for Login.groovy

package com.nbos.core

class Login {

    def springSecurityService

    String username
    String password

    boolean enabled;
    boolean accountExpired;
    boolean accountLocked;
    boolean passwordExpired;

    static constraints = {
        username blank: false, unique: true
        password blank: false
    }

    static mapping = {
        password column: 'password'
    }

    def getAuthorities() {
        [];
    }

    def beforeInsert() {
        encodePassword()
    }

    def beforeUpdate() {
        if (isDirty('password')) {
            encodePassword()
        }
    }

    protected void encodePassword() {
        password = springSecurityService.encodePassword(password)
    }

    @Override
    def String toString(){
        "Login[${id},${username}]"
    }
}

Register a user in grails-app/conf/Bootstrap.groovy as follows:

def init = { servletContext ->
.......
......
  new Login(username: 'abc',password: 'abc').save(flush: true)
}

Set userLookup domain class as Login.groovy in grails-app/conf/config.groovy

grails.plugin.springsecurity.userLookup.userDomainClassName = 'com.nbos.core.Login'

Our next step is to get the resource owner password grant.Send a request with curl

> curl –X POST –d {
"grant_type":"password",
"client_id":"my-client",
"client_secret":"my-secret",
"username":"abc",
"password":"abc",
"scope":"read"
}
 http://localhost:8080/sample-oauth2-app/oauth/token

The access_token is included in response

{
access_token: "b43138dc-aad1-4dc9-9071-8d8230b15c0a"
token_type: "bearer"
refresh_token: "2110e67b-9739-4cf3-908a-7332c7d0df43"
expires_in: 43199
scope: "read"
}

4.Controlling Access To Resources

Access to resources is controlled by the Spring Security Core plugin’s access control mechanisms.This plugin has full support for the OAuth 2.0 SPeL extensions provided by the underlying Spring library. Refer to the methods in OAuth2SecurityExpressionMethods for what is available in the plugin.
Using SPeL is the only tested and confirmed way to enforce OAuth 2.0 specific restrictions on resource access.

Create a Sample controller

grails create-controller com.nbos.core.Sample

Add action to Sample controller which are protected with spring security

package com.nbos.core
import grails.plugin.springsecurity.annotation.Secured

class SampleController {

    static responseFormats = ['json', 'xml']

    @Secured("#oauth2.isClient()")
    def securedWithClientOAuth() {
        def map=['sucsess':'AuthenticationSucces','message':'Client is authenticated successfully']
        respond map
    }

    @Secured("#oauth2.isUser()")
    def securedWithUserOAuth(){
        def map=['sucsess':'AuthenticationSucces','message':'User is authenticated successfully']
        respond map
    }

}

Instruction

Creating API for a Rails application is very easy for a Ruby on Rails developer. But how other customers/clients will know whether the API is working fine or not without a client-side application. Is there any solution for this which I can document for API within the Rails application, The answer is yes we have many tools & approaches but I would prefer swagger UI.

In this article I am going to explain how to create Rails API documentation using swagger UI.

Prerequisites

To understand this article we need a rails application which already have an API. I am going to use my the application which I have created in my previous article

Step1: Install required gems

To Integrate swagger UI for Rails API I am using a gem called swagger-docs. Add this gem to your Gemfile in your application as follows:

   #File Gemfile
   .
   .
   # Add this line
   gem 'swagger-docs'
  

Save the file and run ‘bundle install’. It will install the swagger-docs dependent gems into your system.

Step2: Create a configuration file

After installing the gem create an initializer in config/initializers (e.g. swagger_docs.rb) and define your APIs:

  #File config/initializers/swagger_docs.rb
  Swagger::Docs::Config.register_apis({
  "1.0" =>  {
  	#:controller_base_path => "/app/controllers/api/v0",
    # the extension used for the API
    :api_extension_type => :json,
    # the output location where your .json files are written to
    :api_file_path => "public",
    # the URL base path to your API
    :base_path => "http://localhost:3000",
    # if you want to delete all .json files at each generation
    :clean_directory => true,
    # add custom attributes to api-docs
    :attributes => {
      :info => {
        "title" => "NBOS Rails Oauth provider application",
        "description" => "Rails API documention with Swagger UI.",
        "termsOfServiceUrl" => "http://nbostech.com/",
        "contact" => "contact@nbostech.com"
      }
    }
  }
})
 

Above is minimal configuration to generate API documentation.

Configuration options
The following table shows all the current configuration options and their defaults. The default will be used if you don’t supply your own value.

Option Description Default
api_extension_type The extension, if necessary, used for your API – e.g. :json or :xml nil
api_file_path The output file path where generated swagger-docs files are written to. public/
base_path The URI base path for your API – e.g. api.somedomain.com /
base_api_controller / base_api_controllers The base controller class your project uses; it or its subclasses will be where you call swagger_controller and swagger_api. An array of base controller classes may be provided. ActionController::Base
clean_directory When generating swagger-docs files this option specifies if the api_file_path should be cleaned first. This means that all files will be deleted in the output directory first before any files are generated. false
formatting Specifies which formatting method to apply to the JSON that is written. Available options: :none, :pretty :pretty
camelize_model_properties Camelizes property names of models. For example, a property name called first_name would be converted to firstName. true

Step3: Documenting a controller

Now it’s time to document our API controllers to generate json files. Now I am going to add the documentation for available methods in my registration API controller as follows:

   #File app/controllers/api/v0/registrations_controller.rb
   class Api::V0::RegistrationsController < Devise::RegistrationsController
      skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }
      respond_to :json

      #Add this line
      swagger_controller :registrations, "Registrations"

      #Add this swagger_api block
      swagger_api :create do
         summary "Creates a new User"
         param :form, :first_name, :string, "First name"
         param :form, :last_name, :string, "Last name"
         param :form, :email, :string, :required, "Email address"
         param :form, :password, :string, :required, "Password"
         param :form, :password_confirmation, :string, :required, "Password Confirmation"
         response :unauthorized
         response :not_acceptable
      end
     .
     .
     .
   end
  

So like this add swagger_api block & swagger_controller to required API which you have created. I have added for my sessions API controllers as follows:

  #File app/controllers/api/v0/sessions_controller.rb
  class Api::V0::SessionsController < Devise::SessionsController
    skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }
    skip_before_filter :verify_signed_out_user
    respond_to :json

    #Add this line
    swagger_controller :sessions, "Login and Logout"

    #Add this block
    swagger_api :create do
      summary "Login"
      param :form, :email, :string, :required, "Email address"
      param :form, :password, :string, :required, "Password"
      response :unauthorized
      response :not_acceptable
    end

    swagger_api :destroy do
      summary "Logout"
      param :path, :id, :integer, :required, "User ID"
      response :unauthorized
      response :not_acceptable
    end
  end

   .
   .
 

swagger_controller & swagger_api are helpers to generate swagger UI documentation. Following table will tells more about the DSL of swagger_api block:

Method Description
summary The summary of the API
notes (optional) The associated notes for the API
param Standard API Parameter
param_list Standard API Enum/List parameter.
response Takes a symbol or status code and passes it to Rack::Utils.status_code. The current list of status codes can be seen here: https://github.com/rack/rack/blob/master/lib/rack/utils.rb. An optional message can be added.

Step4: Generate json files

We have created required configuration & annotations for our API methods to generate JSON files. To generate JSON files file our API we have to run swagger rake task as follows from our rails root directory:

   rake swagger:docs
  

After running above rake task Swagger-ui JSON files should now be present in your api_file_path (e.g. ./public/api/v1) which we have configured in config/initializers/swagger_docs.rb file. The swagger-docs rake task (rake swagger:docs) then examines your controllers to determine which controllers should be documented for swagger-ui.

Additional logging for generation failures

Errors aren’t displayed by default. To see all error messages use the SD_LOG_LEVEL environment variable when running the rake task:

  SD_LOG_LEVEL=1 rake swagger:docs

Currently only constantize errors are shown.Errors are written to $stderr. Error logging methods can be found in Config and can be overridden for custom behaviour.

Now we can access the each json file from browser as “http://localhost:3000/api/v0/registrations.json” because they have generated as static assets in public directory.

Step5: Integration of Swagger UI view

Now it’s good idea to integrate swagger UI index view like sample petstore. To do this I have copied all the swagger UI related source files from here. Copy swagger-ui directory to under your rails project “public/api/api” directory and index.html file to “public/api”. Now all the swagger UI resource files will be available under “public/api/api” and swagger-ui home page under “public/api”. Our rails application public directory will look like this:

swagger_doc_directory_structure

Now we all are set. Ready to view the Swagger-UI documentation for our rails application API. IF you are running the the server stop and restart the rails server. Go to browser and try to access “http://localhost:3000/api/index”. Following are the screens. you can click on available links(show/hide, List operations, Expand Operations and Raw) on each resource(registrations, sessions):

swagger_ui_2

swagger_ui_1

swagger_ui_3

For more information about swagger UI and swagger-docs gem visit following websites:

swagger UI

swagger-docs gem

Thanks for reading this article. We hope this was useful!

The intent is to implement OAuth2 provider protocol & secure appropriate rest api’s using OAuth2 token.

The generic need for any application would be to have some public API’s that can be accessed without any security, & some secured using token associated with clientId/secretKey & some secured using token associated with user credentials.

The following are the rest endpoints supporting OAuth2, which provide a token using different protocols, i.e Authorization Code Grant flow (1a,1b), Implicit Grant flow (2), Resource Owner Password Grant (3), Client Credentials Grant (4), Refresh Token Grant(5).

  • OAuth2 Rest End Points 
      1. [ GET] /oauth/authorize:
        • (1a) OAuth2 Authorization Code Grant
          parameters [ ?response_type=code&client_id=my-client&scope=read ]
        • (2) OAuth2 Implicit Grant
          parameters [ ?response_type=token&client_id=my-client&scope=read ]
      2. [ POST] /oauth/token:
            • (1b) OAuth2 Authorization Code Grant
              parameters [ ?grant_type=authorization_code&code=139R59&client_id=my-client ]
            • (3) OAuth2 Resource Owner Password Grant.
              parameters [ ?grant_type=password&client_id=..&client_secret=my-secret&username=aa&password=bb&scope=read ]
            • (4) OAuth2 Client Credentials Grant
              parameters [ ?grant_type=client_credentials&client_id=my-client&scope=read ]
            • (5) OAuth2 Refresh Token Grant
              parameters [ ?grant_type=refresh_token&refresh_token=22c3f395-690c-4838-a14f-3137e6802db1&client_id=my-client&scope=read ]

The following are the API endpoints we would like to support using the token provided above

    1. Public API’s
      1. [  GET] /api/v0/about
      2. [  GET] /api/v0/sample/about
    2. Secured access using a Client Token
      1. [  GET] /api/v0/sample/securedWithClientOAuth : Sample API secured with client token
      2. [POST] /api/v0/members/signup: Create a new User
      3. [POST] /api/v0/auth/login: Login using username/password credentials
    3. Secured access using User Token
      1. [  GET] /api/v0/sample/securedWithUserOAuth: Sample Secured with user token
      2. [  GET] /api/v0/members: Get list of members supports pagination ?filter=”field1=value,field2=v”&sort=”field2″&offset=15
      3. [  GET] /api/v0/members/{memberId}: Get information about a user
      4. [  PUT] /api/v0/members/{memberId}: update a user
      5. [  GET] /api/v0/auth/logout: logout the user associated with the token

In addition, we would want the api server to respond with the following standard HTTP Status responses across all api calls

  1. HTTP 200: Any successful REST api call along with its specific json data
  2. HTTP 400: Any api that results in validation errors like “invalid username”, “empty password” etc. along with json data with list of property errors
    “{ [propertyName:””, messageCode:””, message:””], [propertyName:””,messageCode:””].. }
  3. HTTP 404: Any non-existent REST api request using json along with json response of
    { messageCode:”api.notfound”, message:”locale based message desc”}
  4. HTTP 500: Any REST api request resulting in internal error along with json response of
    {messageCode:”api.internal.error”, message:”locale based message” }
  5. HTTP 403: Any REST api request resulting in forbidden
  6. HTTP 405: Any REST api request using invalid method i.e using GET instead of POST/PUT

Any REST API that supports pagination should respond data in the following format, with results containing the appropriate list items and accept “filter”,”offset”,”sort” & “order” as parameters.

{ 
      "from":10, 
        "to":15, 
"totalCount":100,
     results: [
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" },
       { prop1:"", prop2:"" }
     ]
}

Here is sample input/output for the api endpoints for successful responses

Input Output
[POST] /api/v0/members/signup: Create a new User
curl –X POST –d ‘{
“clientId”:”my-client”,
“username”:”abcdef”,
“password”:”123456″,
“email”:”abcd@ef.com”,
“firstName”:”abcd”
}’
{“member”: {
“description”: “”,
“email”: “abcd@ef.com”,
“firstName”: “abcd”,
“id”: 5,
“lastName”: “”,
“phone”: 0,
“socialAccounts”: []
},
“token”: {
“access_token”: “7224..”,
“expires_in”: 43199,
“refresh_token”: “ebff..”,
“scope”: “read,write”,
“token_type”: “bearer”
} }
[POST] /api/v0/auth/login: Member Login
curl –X POST –d ‘{
“clientId”: “my-client”,
“username”: “abcdef”,
“password”: “123456”
}’
{“member”: {
“description”: “”,
“email”: “abcd@ef.com”,
“firstName”: “abcd”,
“id”: 5,
“lastName”: “”,
“phone”: 0,
“socialAccounts”: []
},
“token”: {
“access_token”: “7224..”,
“expires_in”: 43199,
“refresh_token”: “ebff..”,
“scope”: “read,write”,
“token_type”: “bearer”
} }
[  GET] /api/v0/members/{memberId}: Get Member
curl -H     “Authorization: Bearer 7224…” http://../api/v0/members/5 {
“description”: “”,
“email”: “abcd@ef.com”,
“firstName”: “abcd”,
“id”: 5,
“lastName”: “”,
“phone”: 0,
“socialAccounts”: []
}
[  PUT] /api/v0/members/{memberId}: Update Member
curl –X PUT –H “Authorization: Bearer 7224…”
-d { “id”: 5, “firstName”: “fname”, “lastName”: “lname”,”phone”: 0, “description”: “desc”} http://../api/v0/members/5
{
“description”: “desc”,
“email”: “abcd@ef.com”,
“firstName”: “fname”,
“id”: 5,
“lastName”: “lname”,
“phone”: 0,
“socialAccounts”: []
}


We will walk thru the implementation in the following sequence.

      1. Implement OAuth2 endpoints using spring security oauth2 provider plugin along with sample endpoints access controlled by client/user tokens
      2. Annotate your API with Swagger-UI
      3. Create API for Member Create/Read/Update
      4. Write Unit tests for Member Create/Read/Update
      5. Create API for Auth login/logout & Write Unit Tests
      6. Integrating with Email plugin

Before we start ensure that your environment is all set

      • Install Grails 2.4.5
      • Create a starter app using grails
        mkdir ~/work;
        cd ~/work;
        grails create-app starter-app-rest-grails;
        cd starter-app-rest-grails;
        grails run-app;  // you should now see
        ...
        ======================================================================
        | Server running. Browse to http://localhost:8080/starter-app-rest-grails
        

1. Create API for Member Create/Read/Update

Lets make a list of features that we want, just so it would guide us in our design.

  Feature
Login Domain
1a Ensure that password is getting encrypted while saving
1b We shouldn’t be able to create new Login with same username
1c We should have the ability to expire an account
1d We should have the ability to lock an account
Member Domain
2a Each member should have a unique UUID to identify them using URL
2b We cannot have two members with same email
2c A Member cannot exist without a Login
API / MemberController / create
3a Registration with an existing username should respond with 400
3b Registration with an existing email should respond with 400
3c Invalid email syntax should return 400
3d Successful registration should respond with 200 along with member info

Given the list of features, we will start off with the following ER Model

login-member-ermodel

Our next step is to create a domain classes in grails.

grails create-domain-class com.nbos.core.Login
grails create-domain-class com.nbos.core.Member

Add the necessary properties for Login.groovy

package com.nbos.core

class Login {

    def springSecurityService

    String username
    String password

    boolean enabled;
    boolean accountExpired;
    boolean accountLocked;
    boolean passwordExpired;

    static constraints = {
        username blank: false, unique: true
        password blank: false
    }

    static mapping = {
        password column: 'password'
    }

    def getAuthorities() {
        [];
    }

    def beforeInsert() {
        encodePassword()
    }

    def beforeUpdate() {
        if (isDirty('password')) {
            encodePassword()
        }
    }

    protected void encodePassword() {
        password = springSecurityService.encodePassword(password)
    }

    @Override
    def String toString(){
        "Login[${id},${username}]"
    }
}

Add the necessary properties for Member.groovy

package com.nbos.core

import org.apache.commons.lang.builder.HashCodeBuilder

class Member {

    Login login
    String uuid
    String firstName
    String lastName
    String email
    Long phone
    String description

    def memberService

    static belongsTo = [login:Login]

    static constraints = {
        email(nullable: true)
        lastName(nullable: true)
        phone(nullable: true)
        description(nullable: true)
        uuid(nullable: true)
    }

    static mapping = {
    }

    @Override
    boolean equals(other){
        if (!(other instanceof Member)) return false;
        if ( id &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp; other.id &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp; id == other.id ) return true;
        if ( email == other.email ) return true;
        return false;
    }

    @Override
    int hashCode(){
        def builder = new HashCodeBuilder();
        if(id) { builder.append(id) }
        else { builder.append(email) }
        builder.toHashCode()
    }

    def beforeInsert() {
        uuid = memberService.generateUUID()
    }

    @Override
    def String toString() {
        "member: ${id}, ${login}"
    }
}

Time to create service classes

grails create-service com.nbos.core.MemberService
grails create-service com.nbos.core.TransactionService

And its simple implementation for each of these.

package com.nbos.core

class MemberService {
    def generateUUID() {
        UUID.randomUUID().toString()
    }
}

The intent of creating TransactionService is to help us in unit testing of controllers, the idea is, we could check that “transactionService.rollback” is being called in case of exceptions.

package com.nbos.core

import org.springframework.transaction.TransactionStatus

class TransactionService {

    def rollback(TransactionStatus status) {
        status.setRollbackOnly();
    }
}

Lets Create our first API to create a Member

grails create-controller com.nbos.core.MemberRestfulController

Add an action to create Member.

package com.nbos.core

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import grails.transaction.Transactional
import grails.validation.Validateable

class MemberRestfulController {
    static responseFormats = ['json', 'xml']
    static allowedMethods = [create: 'POST']

    static namespace = 'v0'

    def transactionService
    def memberService
    def mailService
    def securityService

    def create() {
        MemberRegistrationCommand cmd = new MemberRegistrationCommand()
        bindData(cmd, request.getJSON())
        try {
            if (!cmd.validate()) {
                response.setStatus(400)
                respond cmd.errors.collect { [ messageCode:it.code, message:message(code:it.code,args:it.arguments), propertyName:it.field] }
                return
            }
            Member m = cmd.createMember()
            respond [ firstName:m.firstName, lastName:m.lastName, id:m.id, email:m.email ]

        } catch (e) {
            transactionService.rollback(transactionStatus)
            response.setStatus(500)
            log.error("Signup Internal Error : ${e.getMessage()}")
            respond [messageCode:'internal.error', message:message(code:'internal.error',args:[])]
        }
    }
}

@Validateable
class MemberRegistrationCommand {
    String clientId
    String username
    String email
    String password
    String firstName
    String lastName
    static constraints = {
        clientId nullable:true, validator: { val, cmd, errors -&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
            Object[] args = new Object[1]
            args[0] = val
            if(!val){
                errors.rejectValue("clientId", "clientId.empty", args, null)
            }
        }
        username nullable: true, validator: { val, cmd, errors -&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
            Object[] args = new Object[1]
            args[0] = val
            if (!val) {
                errors.rejectValue("username", "user.username.null", args, null)
            } else if (Login.findByUsername(val)) {
                errors.rejectValue("username", "user.username.exists", args, null)
            }
        }
        email nullable: true, validator: { val, cmd, errors -&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
            Object[] args = new Object[1]
            args[0] = val
            if (val) {
                def pattern = /[_A-Za-z0-9-]+(.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(.[A-Za-z0-9]+)*(.[A-Za-z]{2,})/
                if ( ! val ==~ pattern ) {
                    errors.rejectValue("email", "user.email.wrongformat", args, null)
                } else {
                    def member = Member.findByEmail(val)
                    if (member) {
                        errors.rejectValue("email", "user.email.exists", args, null)
                    }
                }
            }
        }
        password nullable: true, validator: { val, cmd, errors -&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
            Object[] args = new Object[1]
            args[0] = val
            if (!val) {
                errors.rejectValue("password", "user.password.null", args, null)
            }
        }
        firstName nullable: true, validator: { val, cmd, errors -&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
            Object[] args = new Object[1]
            args[0] = val
            if (!val) {
                errors.rejectValue("firstName", "user.firstName.null", args, null)
            }
        }
        lastName nullable: true
    }

    def createMember() {
        Login login = new Login(username: username, password: password, enabled: true)
        login.save(failOnError: true, flush: true)
        Member m = new Member(login: login, firstName: firstName, lastName: lastName, email: email)
        m.save(failOnError: true, flush: true)
    }
}

 

We will continue this post with creating tests for the features that we have built.