Skip to content
Eduard Martini edited this page Jun 20, 2013 · 2 revisions

Introduction

This project contains the open source Secure API Grail Pluing that allows you to easily add authorization to your provided API.

I created this plugin because there are several plugins that handles the client side (for connecting to already secured APIs), but none for the API providers.

Secure API Grail Pluing provides the following Grails artefacts:

  • SecureApiService - A service that expose a single method isAuthorizeRequest to be used in your filters or interceptor before executing and API call.

Installation

Declare the plugin dependency in the BuildConfig.groovvy file, as shown here:

grails.project.dependency.resolution = {
    inherits("global") { }
  	log "info"
		repositories {
				//your repositories
		}
		dependencies {
				//your regular dependencies
		}
		plugins {
				//here go your plugin dependencies
				runtime ':secureapi:1.2'
		}
}

Or download the zip packed plugin from here and install with with grails install-plugin command

Config

Add your clients pairs of key/secret in grails-app/conf/Config.groovy_:

[REQUIRED]
grails.secure.api.consumers = ["my_first_api_user": "the_secret_of_my_first_api_user", "my_second_api_user": "the_secret_of_my_second_api_user"]

Getting started with a demo app

If you want to quickly run the plugin on a demo app, you can download Secure Api Sample Implementation App and start it on port 80.

If you want to easily test the implementation using a GUI, you can download Secure API Simple Client App. This app can be used as sample client implementaion for your API users.

Usage

This plugin expose a single method:

###isAuthorizeRequest

as part of SecureApiService. This method takes as a parameter a HttpRequest and performs authentication on it. (check for valid key, valid signature, valid HTTP verb and valid timestamp).

Should be used in a filter protecting your API controller or in the interceptor of your API controller. It works with http status codes. Returns 200 if the request is authorized, 401 if the signature is invalid.

Example:

Add this in your API controller:

class ApiController {
    def secureApiService

    def beforeInterceptor = {
        def isAuthorized = secureApiService.isAuthorizedRequest(request)
        if (isAuthorized == 200) {
            return true
        } else {
            render status: isAuthorized
            return false
        }
    }
    //your API methods .......
}

That's all, now your API is protected.

How it works?

Secure API Grails Plugin implements a simple way to sign API requests in an Oauth 1.1 way, but simplified. This very same method it used by Amazon for the Amazon Web Services.

The client constructs the API request:

https://your.awesome.api/getProduct/1

Adds his key and the timestamp:

https://your.awesome.api/getProduct/1?key=the_public_key&timestamp=UNIX time in ms

Adds the http verb in front: GEThttps://your.awesome.api/getProduct/1?key=the_public_key&timestamp=1371725366979

Do the url encoding on that string and then sign it using HMAC-SHA1 using his secret. Do Base64 encoding on the signature and attach it as final parameter. Then issue the final request to your server:

https://your.awesome.api/getProduct/1?key=the_public_key&timestamp=1371725366979&signature=GaJ%2F66LU%2BHBRfvlMcQK%2Fb1hF3MA%3D

When the request will hit your Grails app, the interceptor will get the request and will pass it to the SecureApiService, where starts the authorization. It verifies if it has a valid key. Then it verifies the valid timestamp. Then strips the signature, re-constructs the string (by adding the request method on begining and url encoding it) then computes the signature using the secret for the provided key. If the obtained signature matches the one received as parameter, returns 200 and the incerceptor should allow the flow to continue and process the request. Else returns 401 and the interceptor should stops the processing of the request.

Why not OAuth?

Secure API Grails plugin is meant for server to server API where no user interaction is need it for authorization. It is simpler and faster because just removes the step of token exchange.

Known bugs:

  • It works only for API providers on port 80. If there is interest on supporting any arbitrary port, I will implement it
  • It is vulnerable to replay attacks. This shouldn't be an issue if the communication is over https. Also I can add a nonce parameter if there is request for it.