Skip to content

Commit

Permalink
Fixes and examples to get OAuth scenarios working with APIcast + prov…
Browse files Browse the repository at this point in the history
…ide Sample Authorization server for testing
  • Loading branch information
Maria Pilar Guerra Arias committed Nov 25, 2016
1 parent aa90a58 commit 732b581
Show file tree
Hide file tree
Showing 17 changed files with 465 additions and 8 deletions.
2 changes: 2 additions & 0 deletions apicast/conf.d/apicast.conf
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ location = /_threescale/oauth_store_token {
internal;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host "$backend_host";

proxy_pass $backend_endpoint/services/$service_id/oauth_access_tokens.xml?$backend_authentication_type=$backend_authentication_value;
}
Expand All @@ -108,6 +109,7 @@ location = /_threescale/check_credentials {

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host "$backend_host";

proxy_pass $backend_endpoint/transactions/oauth_authorize.xml?$backend_authentication_type=$backend_authentication_value&service_id=$service_id&$args;
}
Expand Down
6 changes: 3 additions & 3 deletions apicast/src/get_token.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
local cjson = require 'cjson'
local ts = require 'threescale_utils'
local util = require 'util'
local split = util.string_split

-- As per RFC for Authorization Code flow: extract params from Authorization header and body
-- If implementation deviates from RFC, this function should be over-ridden
Expand All @@ -10,7 +12,7 @@ local function extract_params()
params.authorization = {}

if header_params['Authorization'] then
params.authorization = ngx.decode_base64(header_params['Authorization']:split(" ")[2]):split(":")
params.authorization = split(ngx.decode_base64(split(header_params['Authorization']," ")[2]),":")
end

-- TODO: exit with 400 if the request is GET
Expand Down Expand Up @@ -46,8 +48,6 @@ local function request_token(params)
end
end



-- Check valid params ( client_id / secret / redirect_url, whichever are sent) against 3scale
local function check_client_credentials(params)
local res = ngx.location.capture("/_threescale/check_credentials",
Expand Down
20 changes: 20 additions & 0 deletions examples/oauth2/.env-sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# URI to fetch gateway configuration from. Expected format is: https?://[password@]hostname
THREESCALE_PORTAL_ENDPOINT=https://ACCESS_TOKEN@YOUR_DOMAIN-admin.3scale.net


# Path to a file mounted into docker container that contains the gateway configuration.
# That can be for example cached response from the API.
# THREESCALE_CONFIG_FILE

# Redis host. Used to store access tokens.
REDIS_HOST=redis
# REDIS_PORT=6379

# Limit to subset of services. Comma separated list of service ids.
# APICAST_SERVICES=265,31,42


# What to do when Apicast does not have configuration. Allowed values are: log, exit
# APICAST_MISSING_CONFIGURATION=log

IMAGE_NAME=apicast-test
78 changes: 78 additions & 0 deletions examples/oauth2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
Running APIcast with OAuth
==========================

The API Gateway has a dependency on Redis when adding OAuth support.

In this case, `docker-compose` has to be run in order to start up all of the required components.

The command to do so is:

```shell
docker-compose up -d
```

from the directory containing the `docker-compose.yml` file (in this case `/apicast/examples/oauth2`).

The `-d` flag starts the containers up in detached mode, if you want to see the output when starting the containers, you should omit this.

In order for the command to run successfully, you will also need a `.env` file with the following content:

```
# URI to fetch gateway configuration from. Expected format is: https?://[password@]hostname
THREESCALE_PORTAL_ENDPOINT=https://access_token@example-admin.3scale.net
# Path to a file mounted into docker container that contains the gateway configuration.
# That can be for example cached response from the API.
# THREESCALE_CONFIG_FILE
# Redis host. Used to store access tokens.
REDIS_HOST=redis
# REDIS_PORT=6379
# Limit to subset of services. Comma-separated list of service IDs.
# APICAST_SERVICES=265,31,42
# What to do when APIcast does not have configuration. Allowed values are: log, exit
# APICAST_MISSING_CONFIGURATION=log
IMAGE_NAME=apicast-test
```

The docker compose file spins up 3 services:

1. APIcast
2. Redis
3. A very simple Authorization Server (auth-server) written in Ruby

3scale setup
------------

To get this working with a 3scale instance


Alternatively, you can use `sample_config.json` to run this example instead.


Requesting an access token
--------------------------

Once you have APIcast configured to point to your local OAuth testing instance, you can use a tool such as [Postman](https://www.getpostman.com) to request an access token.

You will need to have an application set up in 3scale and set the Redirect URL to `https://www.getpostman.com/oauth2/callback`

The Auth URL will be the `/authorize` endpoint on your API Gateway instance.
The Access Token URL will be the `/oauth/token` endpoint on your API Gateway instance.

You can then click <strong>Request Token</strong> to initiate the access token request process.

auth-server.rb
--------------

A very simple Sinatra app acting as an Authorization Server.

The app will display a log in page (`/auth/login`) which will accept any values for username and password.
Once logged in, a consent page will be displayed to accept or deny the request.

The authorization server will callback APIcast to issue an authorization code on request acceptance and the `redirect_uri` directly on denial.

Once the Authorization Code is sent to the redirect URL (Postman callback endpoint in this case) the exchange of authorization code for an access token is done transparently by Postman behind the scenes.
3 changes: 3 additions & 0 deletions examples/oauth2/auth-server/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.git
.dockerignore
Gemfile.lock
35 changes: 35 additions & 0 deletions examples/oauth2/auth-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Use the barebones version of Ruby 2.3.1.
FROM ruby:2.3.1-slim

# Optionally set a maintainer name to let people know who made this image.
MAINTAINER Maria Pilar Guerra Arias <pguerra@redhat.com>

# Install dependencies:
# - build-essential: To ensure certain gems can be compiled
RUN apt-get update && apt-get install -y build-essential net-tools --fix-missing --no-install-recommends

# Set an environment variable to store where the app is installed to inside
# of the Docker image.
ENV INSTALL_PATH /auth-server
RUN mkdir -p $INSTALL_PATH

# This sets the context of where commands will be ran in and is documented
# on Docker's website extensively.
WORKDIR $INSTALL_PATH

# Ensure gems are cached and only get updated when they change. This will
# drastically increase build times when your gems do not change.
COPY Gemfile Gemfile
RUN bundle install

# Copy in the application code from your work station at the current directory
# over to the working directory.
COPY . .

# Expose a volume so that nginx will be able to read in assets in production.
VOLUME ["$INSTALL_PATH/public"]

# Start server
ENV PORT 3000
EXPOSE 3000
CMD ["ruby", "auth-server.rb"]
7 changes: 7 additions & 0 deletions examples/oauth2/auth-server/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
source 'https://rubygems.org'
gem 'sinatra'
gem 'thin'
gem 'shotgun'
gem 'httpclient'
gem 'json'
gem 'rake'
36 changes: 36 additions & 0 deletions examples/oauth2/auth-server/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
GEM
remote: https://rubygems.org/
specs:
daemons (1.2.4)
eventmachine (1.2.0.1)
httpclient (2.8.2.4)
json (2.0.2)
rack (1.6.5)
rack-protection (1.5.3)
rack
rake (10.4.2)
shotgun (0.9.2)
rack (>= 1.0)
sinatra (1.4.7)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
thin (1.7.0)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
tilt (2.0.5)

PLATFORMS
ruby

DEPENDENCIES
httpclient
json
rake
shotgun
sinatra
thin

BUNDLED WITH
1.13.1
41 changes: 41 additions & 0 deletions examples/oauth2/auth-server/auth-server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'sinatra'

set :bind, '0.0.0.0'
nginx_redirect_uri = "http://localhost:8080/callback?" #nginx callback
enable :sessions
set :session_secret, '*&(^B234'

get("/") do
erb :root
end

get("/auth/login") do
session[:client_id]=params[:client_id]
session[:redirect_uri]=params[:redirect_uri]
session[:scope]=params[:scope]
session[:state] = params[:state]
session[:pre_token] = params[:tok]
erb :login
end

post("/auth/login") do
redirect "/consent"
end

get("/consent") do
@client_id = session[:client_id]
@scope = session[:scope]
erb :consent
end

get("/authorized") do
callback = "#{nginx_redirect_uri}state=#{session[:state]}&redirect_uri=#{session[:redirect_uri]}"
puts callback
redirect callback
end

get("/denied") do
callback = "#{session[:redirect_uri]}#error=access_deniedt&error_description=resource_owner_denied_request&state=#{session[:state]}"
puts callback
redirect callback
end
2 changes: 2 additions & 0 deletions examples/oauth2/auth-server/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require './auth-server'
run Sinatra::Application
12 changes: 12 additions & 0 deletions examples/oauth2/auth-server/views/consent.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<h3>Consent</h3>

<p>Application with id <%= @client_id %> wants <%= @scope %> access to your data. Do you accept?</p>

<form name="input" action="/authorized" method="get">
<br />
<div class="btn-group">
<button type="submit" class="btn">Accept</button>
<button type="submit" formaction="/denied" class="btn">Deny</button>
</div>

</form>
53 changes: 53 additions & 0 deletions examples/oauth2/auth-server/views/layout.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<html lang="en"><head>
<meta charset="utf-8">
<title>Example Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">

<!-- Le styles -->
<link href="/css/bootstrap.css" rel="stylesheet">
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
</style>
<link href="/css/bootstrap-responsive.css" rel="stylesheet">

<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="/js/html5shiv.js"></script>
<![endif]-->

<!-- Fav and touch icons -->
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/ico/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/ico/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/ico/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="/ico/apple-touch-icon-57-precomposed.png">
<link rel="shortcut icon" href="/ico/favicon.png">
</head>

<body>

<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="http://www.3scale.net">API Provider</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li class="active"><a href="#">Home</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>

<div class="container">

<%= yield %>
</body></html>
14 changes: 14 additions & 0 deletions examples/oauth2/auth-server/views/login.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<h3>Login</h3>

<form name="input" action="/auth/login" method="post">
<label for="user">User:</label>
<input type="text" name="user">

<label for="password">Password:</label>
<input type="password" name="password">
<br />
<div class="btn-group">
<button type="submit" class="btn">Sign In</button>
</div>

</form>
5 changes: 5 additions & 0 deletions examples/oauth2/auth-server/views/root.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<h2>Test Authorization Server for APIcast v2 + OAuth</h2>

<p>
This is a simple Authorization Server to use with APIcast for example and testing purposes.
</p>
Loading

0 comments on commit 732b581

Please sign in to comment.